Skip to content

Commit 4d4c932

Browse files
authored
Ignore redacted auth shutdown noise in Codex logs (#409)
- Broaden benign stderr matching for AuthRequired transport shutdowns - Keep orchestration query formatting changes and contract types aligned
1 parent bc45c81 commit 4d4c932

9 files changed

Lines changed: 239 additions & 201 deletions

File tree

apps/server/src/codexAppServerManager.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ describe("classifyCodexStderrLine", () => {
199199
expect(classifyCodexStderrLine(line)).toBeNull();
200200
});
201201

202+
it("ignores redacted AuthRequired transport shutdown noise", () => {
203+
const line =
204+
'2026-04-10T17:26:09.835934Z ERROR rmcp::transport::worker: worker quit with fatal: Transport channel closed, when AuthRequired(AuthRequiredError { www_authenticate_header: "Bearer [REDACTED]" })';
205+
expect(classifyCodexStderrLine(line)).toBeNull();
206+
});
207+
202208
it("keeps unknown structured errors", () => {
203209
const line = "2026-02-08T04:24:20.085687Z ERROR codex_core::runtime: unrecoverable failure";
204210
expect(classifyCodexStderrLine(line)).toEqual({

apps/server/src/codexAppServerManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ const BENIGN_ERROR_LOG_SNIPPETS = [
162162
"state db record_discrepancy: find_thread_path_by_id_str_in_subdir, falling_back",
163163
];
164164
const BENIGN_STDERR_MESSAGE_SNIPPETS = [
165-
'worker quit with fatal: Transport channel closed, when AuthRequired(AuthRequiredError { www_authenticate_header: "Bearer error=\\"invalid_request\\", error_description=\\"No access token was provided in this request\\", resource_metadata=\\"https://mcp.supabase.com/.well-known/oauth-protected-resource/mcp\\""',
165+
"worker quit with fatal: Transport channel closed, when AuthRequired(AuthRequiredError",
166166
];
167167
const RECOVERABLE_THREAD_RESUME_ERROR_SNIPPETS = [
168168
"not found",

apps/server/src/orchestration/Layers/ProjectionOverviewQuery.ts

Lines changed: 115 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import {
2424
toPersistenceSqlError,
2525
type ProjectionRepositoryError,
2626
} from "../../persistence/Errors.ts";
27-
import { ProjectionOverviewQuery, type ProjectionOverviewQueryShape } from "../Services/ProjectionOverviewQuery.ts";
27+
import {
28+
ProjectionOverviewQuery,
29+
type ProjectionOverviewQueryShape,
30+
} from "../Services/ProjectionOverviewQuery.ts";
2831
import { ProjectionState } from "../../persistence/Services/ProjectionState.ts";
2932
import { ProjectionProject } from "../../persistence/Services/ProjectionProjects.ts";
3033
import { ProjectionThread } from "../../persistence/Services/ProjectionThreads.ts";
@@ -75,7 +78,9 @@ const ProjectionThreadPlanSummaryRow = Schema.Struct({
7578
const ProjectionStateDbRowSchema = ProjectionState;
7679
const ProjectionThreadSessionDbRowSchema = ProjectionThreadSession;
7780

78-
function parseGithubRef(serialized: string | null): OrchestrationOverviewThread["githubRef"] | undefined {
81+
function parseGithubRef(
82+
serialized: string | null,
83+
): OrchestrationOverviewThread["githubRef"] | undefined {
7984
if (!serialized) return undefined;
8085
try {
8186
return JSON.parse(serialized) as OrchestrationOverviewThread["githubRef"];
@@ -153,13 +158,14 @@ function hasActionablePlan(
153158
const matchingTurnPlan =
154159
latestTurnId === null
155160
? null
156-
: [...plans]
161+
: ([...plans]
157162
.filter((plan) => plan.turnId === latestTurnId)
158163
.toSorted(
159164
(left, right) =>
160-
left.updatedAt.localeCompare(right.updatedAt) || left.planId.localeCompare(right.planId),
165+
left.updatedAt.localeCompare(right.updatedAt) ||
166+
left.planId.localeCompare(right.planId),
161167
)
162-
.at(-1) ?? null;
168+
.at(-1) ?? null);
163169
if (matchingTurnPlan) {
164170
return matchingTurnPlan.implementedAt === null;
165171
}
@@ -315,109 +321,120 @@ const makeProjectionOverviewQuery = Effect.gen(function* () {
315321
});
316322

317323
const getOverview: ProjectionOverviewQueryShape["getOverview"] = () =>
318-
sql.withTransaction(
319-
Effect.gen(function* () {
320-
const [projectRows, threadRows, latestTurnRows, sessionRows, planRows, stateRows] =
321-
yield* Effect.all([
322-
listProjectRows(undefined),
323-
listThreadRows(undefined),
324-
listLatestTurnRows(undefined),
325-
listSessionRows(undefined),
326-
listPlanRows(undefined),
327-
listProjectionStateRows(undefined),
328-
]);
324+
sql
325+
.withTransaction(
326+
Effect.gen(function* () {
327+
const [projectRows, threadRows, latestTurnRows, sessionRows, planRows, stateRows] =
328+
yield* Effect.all([
329+
listProjectRows(undefined),
330+
listThreadRows(undefined),
331+
listLatestTurnRows(undefined),
332+
listSessionRows(undefined),
333+
listPlanRows(undefined),
334+
listProjectionStateRows(undefined),
335+
]);
329336

330-
const latestTurnByThread = new Map<string, OrchestrationLatestTurn>();
331-
for (const row of latestTurnRows) {
332-
if (!latestTurnByThread.has(row.threadId)) {
333-
latestTurnByThread.set(row.threadId, toLatestTurn(row));
337+
const latestTurnByThread = new Map<string, OrchestrationLatestTurn>();
338+
for (const row of latestTurnRows) {
339+
if (!latestTurnByThread.has(row.threadId)) {
340+
latestTurnByThread.set(row.threadId, toLatestTurn(row));
341+
}
334342
}
335-
}
336343

337-
const sessionByThread = new Map<string, OrchestrationSession>();
338-
for (const row of sessionRows) {
339-
sessionByThread.set(row.threadId, {
340-
threadId: row.threadId,
341-
status: row.status,
342-
providerName: row.providerName,
343-
runtimeMode: row.runtimeMode,
344-
activeTurnId: row.activeTurnId,
345-
lastError: row.lastError,
346-
updatedAt: row.updatedAt,
347-
});
348-
}
349-
350-
const plansByThread = new Map<string, Array<Schema.Schema.Type<typeof ProjectionThreadPlanSummaryRow>>>();
351-
for (const row of planRows) {
352-
const plans = plansByThread.get(row.threadId) ?? [];
353-
plans.push(row);
354-
plansByThread.set(row.threadId, plans);
355-
}
344+
const sessionByThread = new Map<string, OrchestrationSession>();
345+
for (const row of sessionRows) {
346+
sessionByThread.set(row.threadId, {
347+
threadId: row.threadId,
348+
status: row.status,
349+
providerName: row.providerName,
350+
runtimeMode: row.runtimeMode,
351+
activeTurnId: row.activeTurnId,
352+
lastError: row.lastError,
353+
updatedAt: row.updatedAt,
354+
});
355+
}
356356

357-
const projects: OrchestrationOverviewProject[] = projectRows.map((row) => ({
358-
id: row.projectId,
359-
title: row.title,
360-
workspaceRoot: row.workspaceRoot,
361-
defaultModel: row.defaultModel,
362-
scripts: row.scripts,
363-
activeThreadCount: row.activeThreadCount,
364-
createdAt: row.createdAt,
365-
updatedAt: row.updatedAt,
366-
}));
357+
const plansByThread = new Map<
358+
string,
359+
Array<Schema.Schema.Type<typeof ProjectionThreadPlanSummaryRow>>
360+
>();
361+
for (const row of planRows) {
362+
const plans = plansByThread.get(row.threadId) ?? [];
363+
plans.push(row);
364+
plansByThread.set(row.threadId, plans);
365+
}
367366

368-
const threads: OrchestrationOverviewThread[] = threadRows.map((row) => {
369-
const latestTurn = latestTurnByThread.get(row.threadId) ?? null;
370-
return {
371-
id: row.threadId,
372-
projectId: row.projectId,
367+
const projects: OrchestrationOverviewProject[] = projectRows.map((row) => ({
368+
id: row.projectId,
373369
title: row.title,
374-
model: row.model,
375-
runtimeMode: row.runtimeMode,
376-
interactionMode: row.interactionMode,
377-
branch: row.branch,
378-
worktreePath: row.worktreePath,
379-
...(parseGithubRef(row.githubRef) ? { githubRef: parseGithubRef(row.githubRef) } : {}),
380-
latestTurn,
381-
session: sessionByThread.get(row.threadId) ?? null,
370+
workspaceRoot: row.workspaceRoot,
371+
defaultModel: row.defaultModel,
372+
scripts: row.scripts,
373+
activeThreadCount: row.activeThreadCount,
382374
createdAt: row.createdAt,
383375
updatedAt: row.updatedAt,
384-
lastUserMessageAt: row.lastUserMessageAt,
385-
pendingApprovalCount: row.pendingApprovalCount,
386-
pendingUserInputCount: row.pendingUserInputCount,
387-
hasActionablePlan: hasActionablePlan(plansByThread.get(row.threadId) ?? [], latestTurn),
388-
};
389-
});
376+
}));
390377

391-
const updatedAtCandidates = [
392-
...projectRows.map((row) => row.updatedAt),
393-
...threadRows.map((row) => row.updatedAt),
394-
...sessionRows.map((row) => row.updatedAt),
395-
...stateRows.map((row) => row.updatedAt),
396-
];
378+
const threads: OrchestrationOverviewThread[] = threadRows.map((row) => {
379+
const latestTurn = latestTurnByThread.get(row.threadId) ?? null;
380+
return {
381+
id: row.threadId,
382+
projectId: row.projectId,
383+
title: row.title,
384+
model: row.model,
385+
runtimeMode: row.runtimeMode,
386+
interactionMode: row.interactionMode,
387+
branch: row.branch,
388+
worktreePath: row.worktreePath,
389+
...(parseGithubRef(row.githubRef)
390+
? { githubRef: parseGithubRef(row.githubRef) }
391+
: {}),
392+
latestTurn,
393+
session: sessionByThread.get(row.threadId) ?? null,
394+
createdAt: row.createdAt,
395+
updatedAt: row.updatedAt,
396+
lastUserMessageAt: row.lastUserMessageAt,
397+
pendingApprovalCount: row.pendingApprovalCount,
398+
pendingUserInputCount: row.pendingUserInputCount,
399+
hasActionablePlan: hasActionablePlan(
400+
plansByThread.get(row.threadId) ?? [],
401+
latestTurn,
402+
),
403+
};
404+
});
397405

398-
return Schema.decodeUnknownSync(OrchestrationOverviewSnapshot)({
399-
snapshotSequence: computeSnapshotSequence(stateRows),
400-
limits: {
401-
maxProjects: MAX_PROJECTS,
402-
maxThreadsPerProject: MAX_THREADS_PER_PROJECT,
403-
},
404-
projects,
405-
threads,
406-
updatedAt:
407-
updatedAtCandidates.sort((left, right) => (left < right ? 1 : left > right ? -1 : 0))[0] ??
408-
new Date(0).toISOString(),
409-
});
410-
}),
411-
).pipe(
412-
Effect.mapError((cause): ProjectionRepositoryError => {
413-
if (Schema.isSchemaError(cause)) {
414-
return toPersistenceDecodeError("ProjectionOverviewQuery.getOverview:decode")(cause);
415-
}
416-
return isPersistenceError(cause)
417-
? cause
418-
: toPersistenceSqlError("ProjectionOverviewQuery.getOverview:query")(cause);
419-
}),
420-
);
406+
const updatedAtCandidates = [
407+
...projectRows.map((row) => row.updatedAt),
408+
...threadRows.map((row) => row.updatedAt),
409+
...sessionRows.map((row) => row.updatedAt),
410+
...stateRows.map((row) => row.updatedAt),
411+
];
412+
413+
return Schema.decodeUnknownSync(OrchestrationOverviewSnapshot)({
414+
snapshotSequence: computeSnapshotSequence(stateRows),
415+
limits: {
416+
maxProjects: MAX_PROJECTS,
417+
maxThreadsPerProject: MAX_THREADS_PER_PROJECT,
418+
},
419+
projects,
420+
threads,
421+
updatedAt:
422+
updatedAtCandidates.sort((left, right) =>
423+
left < right ? 1 : left > right ? -1 : 0,
424+
)[0] ?? new Date(0).toISOString(),
425+
});
426+
}),
427+
)
428+
.pipe(
429+
Effect.mapError((cause): ProjectionRepositoryError => {
430+
if (Schema.isSchemaError(cause)) {
431+
return toPersistenceDecodeError("ProjectionOverviewQuery.getOverview:decode")(cause);
432+
}
433+
return isPersistenceError(cause)
434+
? cause
435+
: toPersistenceSqlError("ProjectionOverviewQuery.getOverview:query")(cause);
436+
}),
437+
);
421438

422439
return { getOverview } satisfies ProjectionOverviewQueryShape;
423440
});

0 commit comments

Comments
 (0)