{"record":{"id":"6bffe577abbb75ed","repo":"coleam00/Archon","slug":"openai-token-operation-returned-a-non-json-resp","errorCode":null,"errorMessage":"OpenAI token ${operation} returned a non-JSON response (HTTP ${response.status}).","messagePattern":"OpenAI token (.+?) returned a non-JSON response \\(HTTP (.+?)\\)\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/credentials/openai-oauth.ts","lineNumber":211,"sourceCode":"        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\n * missing `id_token` at exchange time (the whole point of owning this flow);\n * on refresh, a response that omits `id_token`/`refresh_token` PRESERVES the\n * previous values instead of degrading the blob.\n */\nfunction credentialsFromTokenResponse(\n  json: OpenAiTokenResponse,\n  operation: 'exchange' | 'refresh',\n  previous?: OAuthCredentials\n): OpenAiOAuthCredentials {\n  const access = typeof json.access_token === 'string' ? json.access_token : '';","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/core/src/credentials/openai-oauth.ts#L193-L229","documentation":"postTokenRequest (openai-oauth.ts:211) throws when OpenAI's token endpoint responds HTTP 200 but the body is not valid JSON — for example an HTML proxy/maintenance/captive-portal page. The flow labels the condition clearly instead of letting response.json() throw a raw SyntaxError that would be mistaken for an Archon bug.","triggerScenarios":"An HTTP 200 response from OPENAI_TOKEN_URL whose body fails JSON.parse — typically an intercepting corporate proxy, captive portal, maintenance page, or misrouted gateway returning HTML.","commonSituations":"Hotel/office captive portal intercepting HTTPS (with weak interception); corporate proxy returning an HTML block page with status 200; a misconfigured local gateway or API-mocking tool returning plain text.","solutions":["Check what your proxy/network returns for the token endpoint (curl -i the URL) and fix interception/allowlisting.","Move the host to a network without captive portals or HTML-rewriting middleboxes.","Disable or reconfigure TLS-intercepting proxies that inject HTML bodies.","Retry from a clean network; the error is environment-caused, not code-caused."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Probe that the token endpoint returns JSON, not an intercepting HTML page\nconst probe = await fetch('https://auth.openai.com/.well-known/openid-configuration').catch(() => null);\nconst ct = probe?.headers.get('content-type') ?? '';\nif (!ct.includes('application/json')) {\n  throw new Error('A proxy/captive portal is intercepting OpenAI traffic; fix the network first.');\n}","typeGuard":"function isNonJsonResponseError(e: unknown): boolean {\n  return e instanceof Error && /returned a non-JSON response \\(HTTP \\d+\\)/.test(e.message);\n}","tryCatchPattern":"try {\n  await refreshToken(token);\n} catch (e) {\n  if (isNonJsonResponseError(e)) {\n    // Environment problem (proxy/HTML body): surface network guidance, not a code bug\n    reportNetworkIssue(e);\n    return;\n  }\n  throw e;\n}","preventionTips":["Allowlist auth.openai.com on proxies so HTML block pages are never returned.","Disable captive-portal/interception middleboxes on hosts performing logins.","Monitor content-type of outbound auth traffic in restricted networks.","When this fires, test the endpoint with curl -i to see the injected HTML and fix the interceptor."],"tags":["network","oauth","openai","proxy","invalid-response"],"backgroundTag":"non-json-response","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}