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

  1. Redo the full OAuth authorization-code flow (PKCE login) so OpenAI issues a fresh id_token.
  2. Verify the token request goes to the official https://auth.openai.com/oauth/token, not a proxy that strips fields.
  3. Pass the previous credential through so its stored id_token is reused during refresh.
  4. 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

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


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/db37fac4d2eac144. Report an issue: GitHub.