coleam00/Archon · error

Failed to extract the ChatGPT account id from the OpenAI acc

Error message

Failed to extract the ChatGPT account id from the OpenAI access token.

What it means

After obtaining access/refresh/id tokens, credentialsFromTokenResponse decodes the ChatGPT account id from the access token (accountIdFromAccessToken, typically from the JWT's chatgpt_account_id claim) and falls back to previous.accountId. It throws when no account id can be derived from either source, since the account id is required for Codex/API calls.

Source

Thrown at packages/core/src/credentials/openai-oauth.ts:251

  }
  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,
  };
}

/**
 * Exchange a pasted authorization code for the full credential blob
 * (access/refresh/expiry, ChatGPT account id, and — unlike Pi — the
 * `id_token`). Throws with a descriptive message on any missing field.
 */
export async function exchangeOpenAiAuthorizationCode(

View on GitHub (pinned to 0773b97458)

Solutions

  1. Decode the access token (base64 JWT payload) and inspect its claims to see what account identifier is present.
  2. Re-run the OAuth login to obtain a fresh ChatGPT-issued access token.
  3. Pass the previous credential so its accountId is reused when only the access token was refreshed.
  4. Confirm the token came from the ChatGPT OAuth flow (auth.openai.com), not a plain API-key exchange that has no account claim.

Example fix

// before
const creds = credentialsFromTokenResponse(apiKeyExchangeJson, 'exchange');
// after: use the ChatGPT OAuth flow token (contains account claim) or supply previous
const creds = credentialsFromTokenResponse(oauthJson, 'exchange', previousCreds);
Defensive patterns

Strategy: validation

Validate before calling

function accountIdFromAccessToken(token) {
  try {
    const payload = JSON.parse(atob(token.split('.')[1].replace(/-/g,'+').replace(/_/g,'/')));
    return payload['https://api.openai.com/auth']?.chatgpt_account_id ?? payload.chatgpt_account_id ?? null;
  } catch { return null; }
}
// pre-check before calling the exchange
if (!accountIdFromAccessToken(newAccessToken) && !previous?.accountId) throw new Error('no account id derivable');

Type guard

function hasAccountId(c: { accountId?: unknown; access?: string }): c is { accountId: string } {
  if (typeof c.accountId === 'string' && c.accountId) return true;
  return !!c.access && !!accountIdFromAccessToken(c.access);
}

Try / catch

try {
  creds = credentialsFromTokenResponse(json, op, previous);
} catch (e) {
  if (e.message.includes('ChatGPT account id')) {
    console.error('access token lacks chatgpt account claim; decode it:', decodeJwtPayload(json.access_token));
    throw new Error('token not from ChatGPT OAuth flow; re-run login');
  }
  throw e;
}

Prevention

When it happens

Trigger: The access token's payload contains no recognizable account-id claim AND the previous credential has no accountId string.

Common situations: Token issued for an API-key style flow rather than the ChatGPT OAuth flow, so no chatgpt account claim exists; an unexpected token format after a provider change; a corrupted or truncated access token; workspace/org membership changes producing a token without the expected claim.

Related errors


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