{"record":{"id":"b56c22132d1874f8","repo":"can1357/oh-my-pi","slug":"failed-to-extract-accountid-from-token","errorCode":null,"errorMessage":"Failed to extract accountId from token","messagePattern":"Failed to extract accountId from token","errorType":"validation","errorClass":"AIError.OAuthError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/openai-codex.ts","lineNumber":212,"sourceCode":"\t\t\t`Token exchange failed: ${formatOpenAICodexTokenEndpointError(tokenResponse.status, bodyText)}`,\n\t\t\t{ kind: \"token-exchange\", status: tokenResponse.status },\n\t\t);\n\t}\n\n\tconst tokenData = (await tokenResponse.json()) as {\n\t\taccess_token?: string;\n\t\trefresh_token?: string;\n\t\tid_token?: string;\n\t\texpires_in?: number;\n\t};\n\n\tif (!tokenData.access_token || !tokenData.refresh_token || typeof tokenData.expires_in !== \"number\") {\n\t\tthrow new AIError.OAuthError(\"Token response missing required fields\", { kind: \"validation\" });\n\t}\n\n\tconst { accountId, email, planType } = getTokenProfile(tokenData.access_token, tokenData.id_token);\n\tif (!accountId) {\n\t\tthrow new AIError.OAuthError(\"Failed to extract accountId from token\", { kind: \"validation\" });\n\t}\n\n\treturn {\n\t\taccess: tokenData.access_token,\n\t\trefresh: tokenData.refresh_token,\n\t\texpires: Date.now() + tokenData.expires_in * 1000,\n\t\taccountId,\n\t\temail,\n\t\torgId: accountId,\n\t\torgName: planType,\n\t};\n}\n\n/**\n * Login with OpenAI Codex OAuth\n */\nexport type OpenAICodexLoginOptions = OAuthController & {\n\t/** Optional originator value for OpenAI Codex OAuth. Default matches OMP Codex request headers. */","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/openai-codex.ts#L194-L230","documentation":"Thrown after a successful token exchange when the decoded/required fields are present but the ID/access token does not yield an accountId (no chatgpt account_id claim). The library needs the ChatGPT account identifier to construct Codex sessions, so a token without it is unusable and is rejected with kind='validation'.","triggerScenarios":"getTokenProfile() parses the access token / id_token JWT claims and accountId comes back falsy — the token was issued without ChatGPT account claims (wrong auth audience, API-key-style token, non-ChatGPT OpenAI account type).","commonSituations":"Account is an org-only or API-only OpenAI account without a ChatGPT subscription; OpenAI changed the JWT claim shape and the CLI predates it; user authorized with a different account type than expected (e.g. team member without Codex access).","solutions":["Ensure you log in with a ChatGPT account that has Codex/plus access, not an API-only account","Update the CLI — newer versions track current OpenAI token claim shapes","Try switching accounts at auth.openai.com and redoing the device login","Inspect your access token's claims (jwt.io) to confirm whether chatgpt_account_id is present"],"exampleFix":"null","handlingStrategy":"validation","validationCode":"// pre-check the account type you will authorize with: it must be a ChatGPT account\n// with Codex access; API-only accounts lack chatgpt_account_id claims.\nconst claims = JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString());\nif (!claims.chatgpt_account_id) throw new Error('Account has no ChatGPT account_id — use a ChatGPT-enabled account');","typeGuard":"function hasAccountId(d: { accountId?: string | null }): d is { accountId: string } {\n  return typeof d.accountId === 'string' && d.accountId.length > 0;\n}","tryCatchPattern":"try {\n  const tokens = await exchangeCodeForToken(code, verifier);\n} catch (e) {\n  if (e instanceof AIError.OAuthError && e.message === 'Failed to extract accountId from token') {\n    console.error('Signed in with an account lacking ChatGPT/Codex claims. Re-login with a ChatGPT account.');\n  } else throw e;\n}","preventionTips":["Log in with a ChatGPT account that has Codex access, not an API-only account","Update the CLI when OpenAI changes JWT claim shapes","Switch to the correct account at auth.openai.com before completing device login","Decode your access token at jwt.io to verify chatgpt_account_id exists"],"tags":["oauth","openai-codex","jwt","missing-claim"],"backgroundTag":"missing-jwt-claim","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}