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
- Decode the access token (base64 JWT payload) and inspect its claims to see what account identifier is present.
- Re-run the OAuth login to obtain a fresh ChatGPT-issued access token.
- Pass the previous credential so its accountId is reused when only the access token was refreshed.
- 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
- Obtain access tokens via the ChatGPT OAuth flow (auth.openai.com), not plain API-key exchanges — only those carry the account claim.
- Keep previous.accountId across refreshes so fallback covers rotation.
- Decode and inspect the JWT payload when integrating; verify the account claim before shipping config changes.
- Watch for OpenAI claim-format changes when upgrading; log the payload (redacted) on failure.
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
- OpenAI token ${operation} response missing access_token/expi
- OpenAI token ${operation} response missing refresh_token.
- OpenAI token ${operation} response did not include an id_tok
- 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/74a15e9c7e42bf26.
Report an issue: GitHub.