{"record":{"id":"12413ac12b81b473","repo":"sigoden/aichat","slug":"invalid-response-data-data-status-status","errorCode":null,"errorMessage":"Invalid response data: {data} (status: {status})","messagePattern":"Invalid response data: (.+?) \\(status: (.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/client/common.rs","lineNumber":532,"sourceCode":"        ) {\n            bail!(\"{message} (status: {code})\")\n        }\n    } else if let Some(error) = data[0][\"error\"].as_object() {\n        if let (Some(status), Some(message)) = (\n            json_str_from_map(error, \"status\"),\n            json_str_from_map(error, \"message\"),\n        ) {\n            bail!(\"{message} (status: {status})\")\n        }\n    } else if let (Some(detail), Some(status)) = (data[\"detail\"].as_str(), data[\"status\"].as_i64())\n    {\n        bail!(\"{detail} (status: {status})\");\n    } else if let Some(error) = data[\"error\"].as_str() {\n        bail!(\"{error}\");\n    } else if let Some(message) = data[\"message\"].as_str() {\n        bail!(\"{message}\");\n    }\n    bail!(\"Invalid response data: {data} (status: {status})\");\n}\n\npub fn json_str_from_map<'a>(\n    map: &'a serde_json::Map<String, Value>,\n    field_name: &str,\n) -> Option<&'a str> {\n    map.get(field_name).and_then(|v| v.as_str())\n}\n\nasync fn set_client_models_config(client_config: &mut Value, client: &str) -> Result<String> {\n    if let Some(provider) = ALL_PROVIDER_MODELS.iter().find(|v| v.provider == client) {\n        let models: Vec<String> = provider\n            .models\n            .iter()\n            .filter(|v| v.model_type == \"chat\")\n            .map(|v| v.name.clone())\n            .collect();\n        let model_name = select_model(models)?;","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/client/common.rs#L514-L550","documentation":"The final fallback of `catch_error` (src/client/common.rs:532): when a non-2xx response body matches none of the recognized error shapes, the library bails with the raw response JSON and HTTP status. It means the upstream returned a failure the client could not interpret as a known provider error format.","triggerScenarios":"Any API call (chat_completions, chat_completions_streaming, embeddings, claude_chat_completions, openai_chat_completions, openai_embeddings) receiving a non-2xx status whose JSON body lacks `error`, `errors[0]`, `data[0].error`, `detail`+`status`, string `error`, or string `message` fields — or a non-JSON/HTML body serialized as a JSON string value.","commonSituations":"Hitting an HTML error page (502/503 from nginx/Cloudflare) behind a reverse proxy; wrong api_base pointing at a non-API URL; server returning empty object `{}` with 500; Cloudflare auth redirects; provider API version mismatch.","solutions":["Inspect the raw JSON and status embedded in the message — it contains the full upstream body.","Verify api_base URL is the correct API endpoint (should return JSON, not HTML).","Check whether a proxy/load balancer is intercepting the request (502/503 HTML pages).","Retry if the status was 5xx — could be a transient upstream failure.","Update the library if the provider changed its error format, or file an issue to add the new error shape to catch_error."],"exampleFix":"// before: api_base points at the web UI\napi_base = \"https://myserver.com\" \n// after: point at the API route\napi_base = \"https://myserver.com/v1\"","handlingStrategy":"fallback","validationCode":"// preflight: endpoint must return JSON\nlet body = reqwest::get(format!(\"{api_base}/models\")).await?.text().await?;\nassert!(body.trim_start().starts_with('{'), \"endpoint returned non-JSON\");","typeGuard":"fn looks_like_html(body: &str) -> bool {\n    body.trim_start().starts_with('<')\n}","tryCatchPattern":"match client.chat_completions(&req).await {\n    Err(e) if e.to_string().starts_with(\"Invalid response data\") => {\n        // unparseable upstream body; retry once, then surface raw body\n        retry_or_report_raw(e)\n    }\n    other => other,\n}","preventionTips":["Never route api_base through HTML-serving proxies; check for 502/503 HTML pages.","Confirm the URL scheme and path (/v1) are correct.","Treat 5xx as transient and retry with backoff.","Keep the library updated so new provider error shapes are recognized."],"tags":["http-error","unexpected-response","api-client","rust"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"82976d349ad97ac9aae0655ad631dace5e2a6385","analyzedAt":"2026-09-09T18:33:06.139Z","contentChangedAt":"2026-09-09T18:33:06.139Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}