{"record":{"id":"0b7468fdc91025c3","repo":"sigoden/aichat","slug":"message","errorCode":null,"errorMessage":"{message}","messagePattern":"\\{message\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/client/common.rs","lineNumber":530,"sourceCode":"            error.get(\"code\").and_then(|v| v.as_u64()),\n            json_str_from_map(error, \"message\"),\n        ) {\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())","sourceCodeStart":512,"sourceCodeEnd":548,"githubUrl":"https://github.com/sigoden/aichat/blob/82976d349ad97ac9aae0655ad631dace5e2a6385/src/client/common.rs#L512-L548","documentation":"Raised by `catch_error` (src/client/common.rs:530) when a non-2xx API response body has a top-level `message` string field but none of the earlier structured shapes (`error` object, `errors[0]`, `data[0].error`, `detail`+`status`) match. The provider's `message` text is surfaced directly to the caller. This is the last structured fallback before the generic 'Invalid response data' error.","triggerScenarios":"chat_completions, embeddings, claude/openai variants, or streaming calls hitting an API that reports errors as `{\"message\": \"...\"}` (common for Anthropic-style or custom REST providers) with a non-2xx status, without accompanying `status`/`detail` fields.","commonSituations":"Anthropic or Claude-compatible gateways returning `{\"type\":\"error\",\"message\":...}` variants; custom OpenAI-compatible servers (Ollama, LM Studio, vLLM) reporting quota/auth/model errors via `message`; rate-limit responses from proxied endpoints.","solutions":["Act on the provider's message text contained in the error (invalid key, model not found, rate limited, etc.).","Confirm the API key and environment variable (<CLIENT>_API_KEY) are set correctly.","Check the requested model name exists on the provider (run fetch_models or check /v1/models).","If it's a rate-limit message, wait and retry with backoff."],"exampleFix":"// before\nlet client = create_openai_compatible_client(\"http://localhost:8080\")?;\n// after: ensure server is running and model loaded\n// curl http://localhost:8080/v1/models first; then retry with a valid model name","handlingStrategy":"try-catch","validationCode":"// preflight: confirm model exists on provider\nlet names = fetch_models(api_base, Some(&api_key)).await?;\nassert!(names.contains(&model_name));","typeGuard":"fn is_message_error(err: &anyhow::Error) -> bool {\n    // catch_error surfaces provider `message` verbatim\n    !err.to_string().is_empty()\n}","tryCatchPattern":"match client.embeddings(&req).await {\n    Err(e) if e.to_string().to_lowercase().contains(\"rate limit\") => tokio::time::sleep(d).await, retry(),\n    Err(e) => return Err(e),\n    Ok(r) => Ok(r),\n}","preventionTips":["Set <CLIENT>_API_KEY correctly and test with curl first.","Confirm requested model names exist via the provider's models endpoint.","Handle 429 messages with exponential backoff.","Keep provider clients matched to provider endpoints so structured error parsing works."],"tags":["http-error","api-client","rust","upstream-provider"],"backgroundTag":"http-error-response","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"}