{"record":{"id":"4b365eb3e76abf1c","repo":"Hmbown/CodeWhale","slug":"operation-returned-http-status-with-content-type-content","errorCode":null,"errorMessage":"{operation} returned HTTP {status} with content type {content_type}; expected JSON{limit}","messagePattern":"(.+?) returned HTTP (.+?) with content type (.+?); expected JSON(.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/oauth.rs","lineNumber":533,"sourceCode":"            joined\n        }\n    };\n    let mut reader = response.take(OAUTH_RESPONSE_BODY_LIMIT + 1);\n    let mut body = Vec::new();\n    reader\n        .read_to_end(&mut body)\n        .with_context(|| format!(\"reading {operation} response\"))?;\n    let truncated = body.len() as u64 > OAUTH_RESPONSE_BODY_LIMIT;\n    if truncated {\n        body.truncate(OAUTH_RESPONSE_BODY_LIMIT as usize);\n    }\n    let parsed = serde_json::from_slice(&body).map_err(|_| {\n        let limit = if truncated {\n            \" (body exceeded the 64 KiB diagnostic limit)\"\n        } else {\n            \"\"\n        };\n        anyhow::anyhow!(\n            \"{operation} returned HTTP {status} with content type {content_type}; expected JSON{limit}\"\n        )\n    })?;\n    Ok((status, parsed))\n}\n\nfn bounded_oauth_error_text(raw: &str) -> String {\n    let mut output = String::with_capacity(raw.len().min(OAUTH_ERROR_DETAIL_LIMIT));\n    let mut previous_was_space = false;\n    let mut written = 0;\n    for character in raw.chars() {\n        let character = if character.is_whitespace() {\n            ' '\n        } else if character.is_control() {\n            continue;\n        } else {\n            character\n        };","sourceCodeStart":515,"sourceCodeEnd":551,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/oauth.rs#L515-L551","documentation":"`parse_oauth_json` validates the raw HTTP response from an OAuth discovery/device-grant/polling request. When the body cannot be parsed as JSON, it fails with the HTTP status, the response content type, and a note on whether the body exceeded the 64 KiB diagnostic limit — so the developer can see whether the server returned an HTML error page, an oversized body, or some other non-JSON payload.","triggerScenarios":"`discover_oauth_endpoints`, `request_device_grant`, or `poll_device_grant` receives an HTTP response whose body fails `serde_json::from_slice` — e.g. an HTML error page from a proxy, a 502 gateway page, a text/plain message, or a body over 64 KiB (flagged as truncated).","commonSituations":"Corporate proxy/captive portal intercepting the OAuth request and returning HTML; wrong issuer URL pointing at a non-OAuth endpoint; server down with an HTML maintenance page; a misconfigured content type on a JSON endpoint that actually returns form-encoded data.","solutions":["Check the reported status/content-type: an HTML 4xx/5xx means the endpoint or network path is wrong — fix the issuer/endpoint URL or proxy","Open the OAuth endpoint in a browser/curl and confirm it returns application/json","If the body exceeded 64 KiB, fetch the endpoint directly and inspect what oversized payload is being served"],"exampleFix":"// before: issuer points at an HTML landing page\n\"issuer\": \"https://example.com\"\n// after: use the real authorization server root\n\"issuer\": \"https://auth.example.com\"","handlingStrategy":"try-catch","validationCode":"let resp = reqwest::get(oauth_endpoint).await?;\nlet ct = resp.headers().get(CONTENT_TYPE).and_then(|v| v.to_str().ok()).unwrap_or(\"\");\nlet body = resp.text().await?;\nif !ct.starts_with(\"application/json\") {\n    anyhow::bail!(\"endpoint did not return JSON (content-type: {ct}) — check issuer/proxy\");\n}\nif body.len() > 64 * 1024 { anyhow::bail!(\"response exceeds 64 KiB diagnostic limit\"); }\nserde_json::from_str::<serde_json::Value>(&body)?;","typeGuard":null,"tryCatchPattern":"match discover_oauth_endpoints(issuer) {\n    Ok(eps) => eps,\n    Err(e) if e.to_string().contains(\"expected JSON\") => {\n        // message embeds status + content type; check for HTML/proxy pages\n        inspect_endpoint_rawly(issuer)?;\n        Err(e)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Verify the issuer/endpoint URL returns application/json before wiring it up (curl -i)","Check for corporate proxies/captive portals that inject HTML into responses","Keep OAuth responses under 64 KiB; investigate any oversized payload at the source","Monitor the status and content-type embedded in the error message — they usually identify the culprit"],"tags":["oauth","http","json","network"],"backgroundTag":"invalid-json-response","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}