{"record":{"id":"913787c2cdb11ee2","repo":"unslothai/unsloth","slug":"chatgpt-returned-an-invalid-authorization-response","errorCode":null,"errorMessage":"ChatGPT returned an invalid authorization response.","messagePattern":"ChatGPT returned an invalid authorization response\\.","errorType":"exception","errorClass":"CodexAuthError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/openai_codex_auth.py","lineNumber":285,"sourceCode":"        error_code = \"\"\n        try:\n            error = response.json().get(\"error\")\n            error_code = error.get(\"code\", \"\") if isinstance(error, dict) else str(error or \"\")\n        except Exception:\n            pass\n        if data.get(\"grant_type\") == \"refresh_token\" and error_code in {\n            \"invalid_grant\",\n            \"invalid_refresh_token\",\n            \"refresh_token_expired\",\n        }:\n            raise CodexReauthorizationRequired(\n                \"ChatGPT authorization is no longer valid. Please reconnect.\"\n            )\n        raise CodexAuthError(\"ChatGPT authorization failed. Please reconnect.\")\n    try:\n        return response.json()\n    except Exception as exc:\n        raise CodexAuthError(\"ChatGPT returned an invalid authorization response.\") from exc\n\n\nasync def _exchange_code(\n    flow: OAuthFlow,\n    code: str,\n    *,\n    verifier: str | None = None,\n    redirect_uri: str | None = None,\n) -> None:\n    if flow.consumed:\n        raise CodexAuthError(\"Authorization callback was already used.\")\n    flow.consumed = True\n    try:\n        body = await _token_request(\n            {\n                \"grant_type\": \"authorization_code\",\n                \"client_id\": OPENAI_CODEX_CLIENT_ID,\n                \"code\": code,","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/openai_codex_auth.py#L267-L303","documentation":"Raised as CodexAuthError when the token endpoint returns HTTP < 400 but the response body is not valid JSON (response.json() throws). This means the server (or an intermediary proxy/captive portal) returned a 200-range response with HTML or plain text instead of the expected JSON token payload, so the original exception is chained via 'from exc'.","triggerScenarios":"A transparent proxy, captive portal, or TLS-terminating middlebox intercepts the auth request and returns an HTML login/consent page with status 200; a misconfigured base URL points at a web server that returns a redirect-followed HTML page; CDN edge responses during incidents; note follow_redirects=False is set, so a 3xx would hit the >= 400 branch instead.","commonSituations":"Corporate networks with SSL inspection; running Studio behind a proxy that rewrites responses; OPENAI_CODEX token URL constants pointing to a wrong host; transient OpenAI outages serving error pages with 2xx status; DNS hijacking on the host.","solutions":["Check for intercepting proxies, captive portals, or SSL inspection between the host and auth.openai.com; bypass them or install the trusted CA properly.","Verify the token endpoint constant resolves to the real OpenAI auth host (curl the endpoint and inspect raw body).","Retry once after network conditions change; if the body is consistently HTML, fix the network/URL rather than retrying.","Report transient occurrences upstream — a 2xx non-JSON body from the real endpoint is an OpenAI-side defect."],"exampleFix":"// before\nasync with httpx.AsyncClient(trust_env=False) as client:  # accidentally honouring env proxy in another code path\n    resp = await client.post(OPENAI_CODEX_TOKEN_URL, json=data)\nreturn resp.json()  # raises on HTML interception page\n\n// after\nasync with httpx.AsyncClient(timeout=30.0, follow_redirects=False, trust_env=False) as client:\n    resp = await client.post(OPENAI_CODEX_TOKEN_URL, json=data)\nif not resp.headers.get(\"content-type\", \"\").startswith(\"application/json\"):\n    raise CodexAuthError(\"ChatGPT returned an invalid authorization response.\")\nreturn resp.json()","handlingStrategy":"retry","validationCode":"# sanity-check network path before invoking the flow (trust_env=False means env proxies are ignored)\nimport httpx, socket\nsocket.gethostbyname(\"auth.openai.com\")  # DNS resolves\nasync with httpx.AsyncClient(trust_env=False, timeout=10.0) as c:\n    r = await c.get(\"https://auth.openai.com/\")\n    assert r.headers.get(\"content-type\", \"\").startswith(\"text/html\") or True","typeGuard":"def is_invalid_response_error(exc: BaseException) -> bool:\n    return isinstance(exc, codex_auth.CodexAuthError) and \"invalid\" in str(exc) and \"response\" in str(exc)","tryCatchPattern":"try:\n    bundle = await do_token_request()\nexcept codex_auth.CodexAuthError as exc:\n    if \"invalid authorization response\" in str(exc):\n        await asyncio.sleep(2)  # possible proxy/transient corruption; one bounded retry\n        bundle = await do_token_request()\n    else:\n        raise","preventionTips":["Ensure direct (non-intercepted) egress to auth.openai.com:443.","Watch for captive portals on new networks before starting auth flows.","Log the chained exception (__cause__) to identify HTML-in-place-of-JSON interception.","Retry at most once — persistent non-JSON bodies mean a network defect, not a flake."],"tags":["oauth","json","proxy","network","codex"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}