{"record":{"id":"faa8a97dbe2b5568","repo":"coleam00/Archon","slug":"openai-token-operation-failed-response-statu","errorCode":null,"errorMessage":"OpenAI token ${operation} failed (${response.status})${errorCode ? `: ${errorCode}` : ''}","messagePattern":"OpenAI token (.+?) failed \\((.+?)\\)(.+?)` : ''\\}","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/credentials/openai-oauth.ts","lineNumber":201,"sourceCode":"  }\n  if (!response.ok) {\n    // Strip the error body down to the OAuth `error` code: this message flows\n    // into the bridge's session.detail (and on to the browser/CLI), and OpenAI\n    // error bodies can carry account identifiers. Never include the raw body.\n    const text = await response.text().catch(() => '');\n    let errorCode = '';\n    try {\n      const parsed = JSON.parse(text) as { error?: unknown };\n      if (typeof parsed.error === 'string') {\n        errorCode = parsed.error;\n      } else if (parsed.error && typeof parsed.error === 'object') {\n        const code = (parsed.error as { code?: unknown }).code;\n        if (typeof code === 'string') errorCode = code;\n      }\n    } catch {\n      // Non-JSON error body — drop it entirely; the status code must suffice.\n    }\n    throw new Error(\n      `OpenAI token ${operation} failed (${response.status})${errorCode ? `: ${errorCode}` : ''}`\n    );\n  }\n  let raw: unknown;\n  try {\n    raw = await response.json();\n  } catch {\n    // An HTTP 200 with a non-JSON body (proxy/maintenance page) must surface\n    // as a labeled error, not a raw SyntaxError mistaken for an Archon bug.\n    throw new Error(\n      `OpenAI token ${operation} returned a non-JSON response (HTTP ${response.status}).`\n    );\n  }\n  return raw as OpenAiTokenResponse;\n}\n\n/**\n * Map a token response onto the stored credential blob. Fails loud on a","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/credentials/openai-oauth.ts#L183-L219","documentation":"postTokenRequest (openai-oauth.ts:201) throws when the OpenAI token endpoint returns a non-2xx status. The message carries the HTTP status plus, when parseable, the OAuth `error` code extracted from a JSON body — the raw body is deliberately stripped because it can carry account identifiers. This is the server-side rejection of the exchange or refresh request.","triggerScenarios":"Calling exchangeOpenAiAuthorizationCode with an invalid/expired/already-used authorization code (400 invalid_grant), or refreshing with a revoked/expired refresh_token (401), or any 4xx/5xx from the token endpoint.","commonSituations":"User pasting a stale redirect URL after retrying the authorize flow (code single-use); refresh token rotated/revoked server-side (user logged out all devices); clock skew invalidating codes; OpenAI returning 5xx during an incident.","solutions":["For invalid_grant on exchange: start a fresh login (new authorize URL) — authorization codes are single-use and short-lived.","For refresh failures (401/invalid_grant): re-run the subscription login to obtain new tokens.","Check the status + error code in the message for the precise OAuth failure reason.","If 5xx, wait and retry; consult OpenAI status for incidents."],"exampleFix":"// before\nrefreshTokens(staleRefreshToken); // 400 invalid_grant\n// after\nawait startOAuth(userId, 'openai'); // fresh login obtains new refresh token","handlingStrategy":"validation","validationCode":"// Validate the pasted authorization input early — codes are single-use and short-lived\nconst parsed = parseOpenAiAuthorizationInput(pastedValue);\nif (!parsed.code || (parsed.state && parsed.state !== flow.state)) {\n  throw new Error('Paste a fresh redirect URL/code from the current login attempt.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await refreshToken(refreshToken);\n} catch (e) {\n  const m = e instanceof Error ? e.message : '';\n  if (/\\(401\\)|invalid_grant/.test(m)) {\n    // Refresh token revoked/expired: full re-login required\n    return startOAuth(userId, 'openai');\n  }\n  if (/\\(5\\d\\d\\)/.test(m)) {\n    return retryWithBackoff(() => refreshToken(refreshToken));\n  }\n  throw e;\n}","preventionTips":["Always use the redirect URL/code from the current authorize attempt — old codes are single-use.","On any invalid_grant during refresh, fall back to an interactive re-login rather than retrying.","Keep clocks synced (NTP) on the host to avoid code/expiry skew.","Rotate refresh tokens only through the provider flow; avoid copying them between installs."],"tags":["oauth","openai","http-error","token-refresh"],"backgroundTag":"oauth-token-rejected","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}