{"record":{"id":"96d36fb416fd2d6a","repo":"can1357/oh-my-pi","slug":"token-response-missing-required-fields","errorCode":null,"errorMessage":"Token response missing required fields","messagePattern":"Token response missing required fields","errorType":"validation","errorClass":"AIError.OAuthError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/registry/oauth/openai-codex.ts","lineNumber":207,"sourceCode":"\t});\n\n\tif (!tokenResponse.ok) {\n\t\tconst bodyText = await tokenResponse.text();\n\t\tthrow new AIError.OAuthError(\n\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","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/registry/oauth/openai-codex.ts#L189-L225","documentation":"Thrown when OpenAI's token endpoint returned 200 but the parsed JSON lacks the fields the Codex flow requires: access_token, refresh_token, or a numeric expires_in. The library treats a structurally incomplete success response as a validation failure rather than handing back unusable credentials. Classified kind='validation'.","triggerScenarios":"The token endpoint responds with a JSON body that is an error object but with HTTP 200, or omits refresh_token (e.g. response for a grant type that doesn't issue refresh tokens), or expires_in is missing/non-numeric; response body is HTML/JSON that doesn't match the expected token shape.","commonSituations":"OpenAI changing token response shape vs an outdated CLI; API gateway/intermediary returning 200 with an error page; headless environments where a captive portal returns 200 HTML; the endpoint returning {error: ...} with a 200 status.","solutions":["Update the CLI to match the current OpenAI token response contract","Log/capture the raw token response body (if your tooling allows) to see what actually came back","Check for proxies or VPNs rewriting responses and bypass them","Retry the login; if consistently reproducible, report with the redacted response shape"],"exampleFix":"null","handlingStrategy":"type-guard","validationCode":"// can't inspect the response before the call, but validate your environment\n// ensure no proxy returns 200 error pages:\n// curl -s -o /dev/null -w '%{http_code} %{content_type}' https://auth.openai.com/oauth/token","typeGuard":"function isWellFormedTokenResponse(d: unknown): d is { access_token: string; refresh_token: string; expires_in: number } {\n  const t = d as Record<string, unknown> | null;\n  return !!t && typeof t.access_token === 'string' && typeof t.refresh_token === 'string' && typeof t.expires_in === 'number';\n}","tryCatchPattern":"try {\n  const tokens = await exchangeCodeForToken(code, verifier);\n} catch (e) {\n  if (e instanceof AIError.OAuthError && e.kind === 'validation' && e.message === 'Token response missing required fields') {\n    // log raw response via network tooling; update CLI; retry login\n  } else throw e;\n}","preventionTips":["Keep the CLI current against OpenAI token response changes","Bypass proxies/VPNs that could substitute 200 error pages","Retry the login once before investigating — some failures are transient","Check for error objects wrapped in HTTP 200 responses when debugging"],"tags":["oauth","openai-codex","validation","malformed-response"],"backgroundTag":"schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}