sigoden/aichat · error

Invalid response data

Error message

Invalid response data: {data} (status: {status})

What it means

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.

Solutions

  1. Inspect the raw JSON and status embedded in the message — it contains the full upstream body.
  2. Verify api_base URL is the correct API endpoint (should return JSON, not HTML).
  3. Check whether a proxy/load balancer is intercepting the request (502/503 HTML pages).
  4. Retry if the status was 5xx — could be a transient upstream failure.
  5. Update the library if the provider changed its error format, or file an issue to add the new error shape to catch_error.

Example fix

// before: api_base points at the web UI
api_base = "https://myserver.com" 
// after: point at the API route
api_base = "https://myserver.com/v1"
Defensive patterns

Strategy: fallback

Validate before calling

// preflight: endpoint must return JSON
let body = reqwest::get(format!("{api_base}/models")).await?.text().await?;
assert!(body.trim_start().starts_with('{'), "endpoint returned non-JSON");

Type guard

fn looks_like_html(body: &str) -> bool {
    body.trim_start().starts_with('<')
}

Try / catch

match client.chat_completions(&req).await {
    Err(e) if e.to_string().starts_with("Invalid response data") => {
        // unparseable upstream body; retry once, then surface raw body
        retry_or_report_raw(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/12413ac12b81b473. Report an issue: GitHub.

Appendix: source

Thrown at src/client/common.rs:532

        ) {
            bail!("{message} (status: {code})")
        }
    } else if let Some(error) = data[0]["error"].as_object() {
        if let (Some(status), Some(message)) = (
            json_str_from_map(error, "status"),
            json_str_from_map(error, "message"),
        ) {
            bail!("{message} (status: {status})")
        }
    } else if let (Some(detail), Some(status)) = (data["detail"].as_str(), data["status"].as_i64())
    {
        bail!("{detail} (status: {status})");
    } else if let Some(error) = data["error"].as_str() {
        bail!("{error}");
    } else if let Some(message) = data["message"].as_str() {
        bail!("{message}");
    }
    bail!("Invalid response data: {data} (status: {status})");
}

pub fn json_str_from_map<'a>(
    map: &'a serde_json::Map<String, Value>,
    field_name: &str,
) -> Option<&'a str> {
    map.get(field_name).and_then(|v| v.as_str())
}

async fn set_client_models_config(client_config: &mut Value, client: &str) -> Result<String> {
    if let Some(provider) = ALL_PROVIDER_MODELS.iter().find(|v| v.provider == client) {
        let models: Vec<String> = provider
            .models
            .iter()
            .filter(|v| v.model_type == "chat")
            .map(|v| v.name.clone())
            .collect();
        let model_name = select_model(models)?;

View on GitHub (pinned to 82976d349a)