{"record":{"id":"49e97424e5b5d0c8","repo":"zeroclaw-labs/zeroclaw","slug":"model-list-failed-at-url-http-status","errorCode":null,"errorMessage":"{} model list failed at {url}: HTTP {status}","messagePattern":"(.+?) model list failed at (.+?): HTTP (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/compatible.rs","lineNumber":2678,"sourceCode":"                        ERROR,\n                        ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                            .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                            .with_attrs(::serde_json::json!({\n                                \"model_provider\": &self.name,\n                                \"url\": &url,\n                                \"phase\": \"model_list_request\",\n                                \"error\": super::format_error_chain(&e),\n                            })),\n                        \"compatible: model list request failed\"\n                    );\n                    anyhow::Error::msg(format!(\n                        \"{} model list request failed: {url}: {e}\",\n                        self.name\n                    ))\n                })?;\n            if !response.status().is_success() {\n                let status = response.status();\n                anyhow::bail!(\"{} model list failed at {url}: HTTP {status}\", self.name);\n            }\n            let body: ModelsResponse = response.json().await.map_err(|e| {\n                ::zeroclaw_log::record!(\n                    ERROR,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                        .with_attrs(::serde_json::json!({\n                            \"model_provider\": &self.name,\n                            \"phase\": \"model_list_parse\",\n                            \"error\": super::format_error_chain(&e),\n                        })),\n                    \"compatible: model list returned invalid JSON\"\n                );\n                anyhow::Error::msg(format!(\n                    \"{} model list returned invalid JSON: {e}\",\n                    self.name\n                ))\n            })?;","sourceCodeStart":2660,"sourceCodeEnd":2696,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/compatible.rs#L2660-L2696","documentation":"OpenAiCompatibleModelProvider::list_models GETs {base_url}/models when a credential resolves or public_model_listing is enabled. The endpoint answered but with a non-2xx status; the message embeds the provider display name, the full URL, and the HTTP status so the failure is directly attributable.","triggerScenarios":"401/403 from a bad or expired API key; 404 when base_url is wrong (missing or doubled /v1, or trailing slash) or the server does not expose /models; 429 rate limiting; 5xx from the upstream gateway.","commonSituations":"base_url set to the host root when the API lives under /v1 (or /v1/v1 after duplication); local OpenAI-compatible servers with different routing; expired tokens; gateways behind flaky proxies.","solutions":["Reproduce directly: curl -i {base_url}/models with the same Authorization header and read the status","Fix base_url so it points at the API root that serves /models (usually ends in /v1, no trailing slash)","Verify the API key is valid and unexpired for that provider","If the server truly has no /models, configure a static model list on the alias or enable the models_dev/openrouter catalog fallback"],"exampleFix":"# before\n[model_provider.local]\nfamily = \"compatible\"\nbase_url = \"http://localhost:11434\"\n\n# after\n[model_provider.local]\nfamily = \"compatible\"\nbase_url = \"http://localhost:11434/v1\"","handlingStrategy":"try-catch","validationCode":"// Sanity-check the URL shape before trusting it for listing\nlet url = reqwest::Url::parse(&format!(\"{base_url}/models\"))?;\nif !url.scheme().starts_with(\"http\") || base_url.ends_with('/') {\n    anyhow::bail!(\"base_url must not end with '/' and must be a valid http(s) root\");\n}","typeGuard":null,"tryCatchPattern":"match provider.list_models().await {\n    Ok(models) => Ok(models),\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.contains(\"HTTP 429\") || msg.contains(\"HTTP 5\") {\n            backoff_retry(/* ... */).await // transient: retry with jitter\n        } else if msg.contains(\"HTTP 401\") || msg.contains(\"HTTP 403\") {\n            /* credential problem: surface to user, do not retry */\n            Err(e)\n        } else {\n            Err(e) // 404 etc: fix base_url / static list\n        }\n    }\n}","preventionTips":["curl the /models endpoint once when adding a new alias","Standardize base_url to end at the version segment (e.g. /v1) with no trailing slash","Keep a static model list on aliases whose servers lack /models","Treat 401 as config drift - alert on it instead of retrying"],"tags":["openai-compatible","models","http","config","rust"],"backgroundTag":"http-error-response","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}