Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/server/responses/compact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ export async function handleResponsesCompact(
if (err instanceof CodexAuthContextError) {
return formatErrorResponse(401, "authentication_error", "Selected Codex account needs reauthentication");
}
if (err instanceof CodexMainSubstitutionUnavailableError) {
return formatErrorResponse(401, "authentication_error", "No usable Codex main credential to serve this request");
}
if (err instanceof CodexPoolAuthenticationError || err instanceof CodexDirectAuthenticationError) {
return formatErrorResponse(401, "authentication_error", err.message);
}
Expand Down
26 changes: 26 additions & 0 deletions tests/codex-envkey-admission-substitution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ async function postResponses(url: string | URL, authorization: string): Promise<
});
}

async function postCompact(url: string | URL, authorization: string): Promise<Response> {
return originalFetch(new URL("/v1/responses/compact", url), {
method: "POST",
headers: { "content-type": "application/json", authorization },
body: JSON.stringify({ model: "gpt-5.6-luna", input: [] }),
});
}

describe("#1686 env_key bearer admission reaches Direct with substitution", () => {
test("an admission bearer is served and the stored main credential goes upstream", async () => {
setDebugSettings({ debug: true });
Expand Down Expand Up @@ -158,6 +166,24 @@ describe("#1686 env_key bearer admission reaches Direct with substitution", () =
}
});

test("compact reports missing substitution credentials as authentication failure", async () => {
saveConfig(directConfig());
writeFileSync(join(codexHome, "auth.json"), JSON.stringify({ tokens: {} }));

const server = startServer(0);
try {
const response = await postCompact(server.url, `Bearer ${ADMISSION_SECRET}`);
const body = await response.json() as { error?: { type?: string; message?: string } };

expect(response.status).toBe(401);
expect(body.error?.type).toBe("authentication_error");
expect(body.error?.message).toBe("No usable Codex main credential to serve this request");
expect(upstreamAuth).toHaveLength(0);
} finally {
await server.stop(true);
}
});

test("a foreign bearer is still Codex Direct passthrough, not admission", async () => {
saveConfig(directConfig());
writeStoredMain(liveJwt());
Expand Down
Loading