coleam00/Archon · error
OpenAI token ${operation} response did not include an id_tok
Error message
OpenAI token ${operation} response did not include an id_token (required by the Codex CLI). What it means
credentialsFromTokenResponse demands an id_token in the token response because the Codex CLI requires it — a credential stored without one reproduces GitHub issue #1924 ('invalid ID token format'). It uses json.id_token, falling back to previous.id_token, and throws when neither exists. This is an intentional fail-loud guard against persisting an unusable credential.
Source
Thrown at packages/core/src/credentials/openai-oauth.ts:244
operation: 'exchange' | 'refresh',
previous?: OAuthCredentials
): OpenAiOAuthCredentials {
const access = typeof json.access_token === 'string' ? json.access_token : '';
const expiresIn = typeof json.expires_in === 'number' ? json.expires_in : NaN;
if (!access || !Number.isFinite(expiresIn)) {
throw new Error(`OpenAI token ${operation} response missing access_token/expires_in.`);
}
const prevRefresh = typeof previous?.refresh === 'string' ? previous.refresh : '';
const refresh = typeof json.refresh_token === 'string' ? json.refresh_token : prevRefresh;
if (!refresh) {
throw new Error(`OpenAI token ${operation} response missing refresh_token.`);
}
const prevIdToken = typeof previous?.id_token === 'string' ? previous.id_token : '';
const idToken = typeof json.id_token === 'string' && json.id_token ? json.id_token : prevIdToken;
if (!idToken) {
// Fail loud: an id_token-less credential reproduces the exact #1924
// breakage ("invalid ID token format" in the Codex CLI) — never store one.
throw new Error(
`OpenAI token ${operation} response did not include an id_token (required by the Codex CLI).`
);
}
const prevAccountId = typeof previous?.accountId === 'string' ? previous.accountId : '';
const accountId = accountIdFromAccessToken(access) ?? prevAccountId;
if (!accountId) {
throw new Error('Failed to extract the ChatGPT account id from the OpenAI access token.');
}
return {
// Preserve any extra fields a future token response taught us to keep.
...(previous ?? {}),
access,
refresh,
expires: Date.now() + expiresIn * 1000,
accountId,
id_token: idToken,
};
}View on GitHub (pinned to 0773b97458)
Solutions
- Redo the full OAuth authorization-code flow (PKCE login) so OpenAI issues a fresh id_token.
- Verify the token request goes to the official https://auth.openai.com/oauth/token, not a proxy that strips fields.
- Pass the previous credential through so its stored id_token is reused during refresh.
- Ensure the client config uses OpenAI's documented scopes/audience so an OIDC id_token is issued.
Example fix
// before: constructing credentials without id_token const creds = credentialsFromTokenResponse(json, 'exchange'); // throws if response lacks id_token // after: fall back to the previously stored credential const creds = credentialsFromTokenResponse(json, 'exchange', storedCreds); // reuse stored id_token
Defensive patterns
Strategy: validation
Validate before calling
const hasIdToken = (j, prev) =>
(typeof j?.id_token === 'string' && j.id_token) || (typeof prev?.id_token === 'string' && prev.id_token);
if (!hasIdToken(json, storedCreds)) throw new Error('token response lacks id_token; redo PKCE login'); Type guard
function hasIdToken(c: { id_token?: unknown }): c is { id_token: string } {
return typeof c.id_token === 'string' && c.id_token.length > 0;
} Try / catch
try {
creds = credentialsFromTokenResponse(json, op, previous);
} catch (e) {
if (e.message.includes('did not include an id_token')) {
creds = await runOpenAiLoginFlow(); // fresh login re-issues id_token
} else throw e;
} Prevention
- Use the official https://auth.openai.com/oauth/token endpoint — proxies can strip id_token.
- Keep the full stored credential (including id_token) across refreshes so the fallback applies.
- After a provider/OIDC config change, do a fresh login instead of refreshing old tokens.
- Test the credential against the Codex CLI early — it is the consumer that fails without id_token.
When it happens
Trigger: The OpenAI token exchange/refresh response contains no id_token field (or an empty string), and the previous credential also has no id_token.
Common situations: Calling the token endpoint with grant types or scopes that skip ID-token issuance; a custom/proxied token endpoint that strips id_token; OpenAI-side changes; building credentials from a partial response saved earlier that already lacked id_token.
Related errors
- OpenAI token ${operation} response missing access_token/expi
- OpenAI token ${operation} response missing refresh_token.
- Failed to extract the ChatGPT account id from the OpenAI acc
- Stored OpenAI credential has no refresh token.
- OAuth state mismatch.
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/db37fac4d2eac144.
Report an issue: GitHub.