{"record":{"id":"14e7d35b53beef15","repo":"Kuberwastaken/claurst","slug":"failed-to-parse-token-response","errorCode":null,"errorMessage":"Failed to parse token response: {}","messagePattern":"Failed to parse token response: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-rust/crates/cli/src/codex_oauth_flow.rs","lineNumber":211,"sourceCode":"    ];\r\n\r\n    let resp = client\r\n        .post(CODEX_TOKEN_URL)\r\n        .form(&params)\r\n        .send()\r\n        .await\r\n        .map_err(|e| anyhow!(\"Failed to exchange code: {}\", e))?;\r\n\r\n    if !resp.status().is_success() {\r\n        let status = resp.status();\r\n        let body = resp.text().await.unwrap_or_default();\r\n        bail!(\"Token exchange failed ({}): {}\", status, body);\r\n    }\r\n\r\n    let body: serde_json::Value = resp\r\n        .json()\r\n        .await\r\n        .map_err(|e| anyhow!(\"Failed to parse token response: {}\", e))?;\r\n\r\n    let access_token = body[\"access_token\"]\r\n        .as_str()\r\n        .unwrap_or(\"\")\r\n        .to_string();\r\n\r\n    if access_token.is_empty() {\r\n        bail!(\"No access_token in response\");\r\n    }\r\n\r\n    let refresh_token = body[\"refresh_token\"].as_str().map(|s| s.to_string());\r\n    let account_id = extract_account_id_from_jwt(&access_token);\r\n\r\n    Ok(CodexTokens {\r\n        access_token,\r\n        refresh_token,\r\n        account_id,\r\n        expires_at: None,\r","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/Kuberwastaken/claurst/blob/b0637c97ec34144387cbf2f74f65df6d16a6cef1/src-rust/crates/cli/src/codex_oauth_flow.rs#L193-L229","documentation":"The token endpoint returned HTTP 2xx but the body could not be deserialized as JSON. The code calls resp.json::<serde_json::Value>() and maps the serde error into this anyhow error. It indicates the successful response was not valid JSON, which is unexpected for a well-behaved OAuth server.","triggerScenarios":"exchange_code_for_tokens receives a 200 response whose body is HTML (proxy/login portal interstitial), empty, truncated, or otherwise non-JSON, so resp.json().await fails.","commonSituations":"Corporate proxy rewriting responses with an HTML error/notice page; misconfigured CODEX_TOKEN_URL pointing at an HTML page; captive portal Wi-Fi intercepting HTTPS; server returning a 200 with empty body during partial outage.","solutions":["Dump the raw response body and inspect it; look for HTML from a proxy or captive portal.","Bypass or properly configure the corporate proxy for this host.","Verify CODEX_TOKEN_URL is the correct OAuth token endpoint and was not changed.","Retry later if the provider is having an incident."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// capture raw body first so failures are debuggable\nlet raw = resp.text().await?;\nlet body: serde_json::Value = serde_json::from_str(&raw)\n    .map_err(|e| anyhow!(\"Failed to parse token response: {} (body: {})\", e, &raw[..raw.len().min(200)]))?;","typeGuard":null,"tryCatchPattern":"// catch serde errors and include the offending body\nmatch serde_json::from_str::<serde_json::Value>(&raw) {\n    Ok(v) => Ok(v),\n    Err(e) => Err(anyhow!(\"non-JSON token response: {}\", e)),\n}","preventionTips":["Read the body as text first and log a prefix on parse failure","Detect HTML responses (starts with '<') and report proxy/captive-portal interference","Pin and verify the token endpoint URL in configuration"],"tags":["oauth","json","token-exchange","http"],"backgroundTag":"json-decode-failed","analyzedSha":"b0637c97ec34144387cbf2f74f65df6d16a6cef1","analyzedAt":"2026-09-10T00:24:58.650Z","contentChangedAt":"2026-09-10T00:24:58.650Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}