{"record":{"id":"09a9d14b0665b055","repo":"Hmbown/CodeWhale","slug":"failed-to-list-models-http-status-error-text","errorCode":null,"errorMessage":"Failed to list models: HTTP {status}: {error_text}","messagePattern":"Failed to list models: HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/client.rs","lineNumber":2105,"sourceCode":"            .to_string();\n\n        Ok(translated)\n    }\n\n    /// List available models from the provider.\n    pub async fn list_models(&self) -> Result<Vec<AvailableModel>> {\n        let url = api_url(&self.base_url, \"models\");\n        let response = self.send_with_retry(|| self.http_client.get(&url)).await?;\n\n        let status = response.status();\n        if !status.is_success() {\n            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;\n            let error_text = sanitize_http_error_body(\n                Some(self.api_provider.display_name()),\n                status.as_u16(),\n                &raw_error_text,\n            );\n            anyhow::bail!(\"Failed to list models: HTTP {status}: {error_text}\");\n        }\n        let response_text = response\n            .text()\n            .await\n            .context(\"Failed to read models response body\")?;\n\n        parse_models_response(&response_text)\n            .map(|models| apply_provider_model_cutline(self.api_provider, models))\n    }\n\n    /// The catalog provider id for this client (the `ProviderKind` slug, falling\n    /// back to the `ApiProvider` slug for legacy variants without a kind). This\n    /// is the id used as the cache scope and `CatalogOffering.provider`.\n    fn catalog_provider_id(&self) -> String {\n        self.api_provider\n            .kind()\n            .map(|kind| kind.as_str().to_string())\n            .unwrap_or_else(|| self.api_provider.as_str().to_string())","sourceCodeStart":2087,"sourceCodeEnd":2123,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/client.rs#L2087-L2123","documentation":"`list_models()` issues `GET {base_url}/models` through the retrying transport and bails when the HTTP status is not 2xx. The response body is read with a 64KB cap and sanitized (provider display name, status code, secret redaction) before being embedded, so the message shows the provider's own error text safely. This is the model-picker's discovery call.","triggerScenarios":"401/403 when the API key is missing, wrong, or lacks scope; 404 when the provider or proxy does not expose `/models` at the configured base_url (wrong `/v1` prefix, gateway without a models route); 429 when rate-limited before listing; 5xx upstream outages.","commonSituations":"Custom OpenAI-compatible servers (llama.cpp, some vLLM/LiteLLM setups) that omit or disable the models listing route; base_url with a doubled or missing version path; expired or mistyped API key; corporate proxies blocking GET on unknown paths.","solutions":["Check the status code in the message: 401/403 → fix the API key for that provider; 404 → the endpoint does not serve `/models`.","Correct the base_url so `{base_url}/models` resolves (verify with `curl -s $BASE_URL/models -H \"Authorization: Bearer $KEY\"`).","If the server genuinely has no models route, configure the model id explicitly instead of relying on listing.","429/5xx: wait and retry; the error text from the provider usually states the quota or outage reason."],"exampleFix":"# before\nbase_url = \"https://my-gateway/api\"          # 404 on /api/models\n# after\nbase_url = \"https://my-gateway/api/v1\"      # /api/v1/models exists","handlingStrategy":"try-catch","validationCode":"// Cheap preflight before rendering a model picker.\nasync fn models_endpoint_ok(base_url: &str, key: &str) -> bool {\n    let url = format!(\"{base_url}/models\");\n    reqwest::Client::new().get(&url).bearer_auth(key).send().await\n        .is_ok_and(|r| r.status().is_success())\n}","typeGuard":null,"tryCatchPattern":"match client.list_models().await {\n    Ok(models) => render_picker(models),\n    Err(e) if e.to_string().contains(\"Failed to list models: HTTP 404\") => {\n        // endpoint has no /models route: fall back to manual model entry\n        render_manual_model_entry(),\n    }\n    Err(e) if e.to_string().contains(\"HTTP 401\") || e.to_string().contains(\"HTTP 403\") => prompt_for_api_key(),\n    Err(e) => show_error(e),\n}","preventionTips":["Treat model listing as best-effort: cache the last successful list and offer manual model entry when it fails.","Verify base_url/key with `codewhale doctor` after changing provider config.","Check `{base_url}/models` with curl when onboarding a new OpenAI-compatible gateway."],"tags":["api","models","discovery","http","configuration"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}