Hmbown/CodeWhale · error · anyhow::Error

Failed to list models: HTTP

Error message

Failed to list models: HTTP {status}: {error_text}

What it means

list_models() issues GET {base_url}/models through the retrying HTTP client; any non-2xx status becomes this error, embedding the status code plus a sanitized, size-capped response body (credentials and provider noise stripped). It is the transport-level failure surface for model discovery.

Solutions

  1. Verify the API key and the environment variable name for the configured provider.
  2. Check the base_url path shape — OpenAI-style APIs usually require it to end in /v1.
  3. On 429/5xx, wait and retry; the client's retry policy already covers transient cases, so a persistent 429 means quota is exhausted.
  4. Read the sanitized body embedded in the message: it usually contains the provider's exact complaint.

Example fix

# before
base_url = "https://api.provider.com"    # GET /models -> 404

# after
base_url = "https://api.provider.com/v1"  # GET /v1/models -> 200
Defensive patterns

Strategy: retry

Try / catch

Parse the embedded status from the message: retry with backoff on 429/500/502/503; fail fast with a credentials message on 401/403; treat 404 as a base_url misconfiguration and stop retrying.

Prevention

When it happens

Trigger: 401/403 with an invalid or missing API key; 404 when base_url has the wrong path shape (missing or doubled /v1); 429 rate limiting; 5xx provider outages; a gateway that does not expose /models at all.

Common situations: Wrong API key environment variable at startup; base URL pointing at the provider root instead of the API root; expired or trial-exhausted keys; rate limits hit right after startup model fetch.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/09a9d14b0665b055. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/client.rs:2202

            .to_string();

        Ok(translated)
    }

    /// List available models from the provider.
    pub async fn list_models(&self) -> Result<Vec<AvailableModel>> {
        let url = api_url(&self.base_url, "models");
        let response = self.send_with_retry(|| self.http_client.get(&url)).await?;

        let status = response.status();
        if !status.is_success() {
            let raw_error_text = bounded_error_text(response, ERROR_BODY_MAX_BYTES).await;
            let error_text = sanitize_http_error_body(
                Some(self.api_provider.display_name()),
                status.as_u16(),
                &raw_error_text,
            );
            anyhow::bail!("Failed to list models: HTTP {status}: {error_text}");
        }
        let response_text = response
            .text()
            .await
            .context("Failed to read models response body")?;

        parse_models_response(&response_text)
            .map(|models| apply_provider_model_cutline(self.api_provider, models))
    }

    /// The catalog provider id for this client (the `ProviderKind` slug, falling
    /// back to the `ApiProvider` slug for legacy variants without a kind). This
    /// is the id used as the cache scope and `CatalogOffering.provider`.
    fn catalog_provider_id(&self) -> String {
        self.api_provider
            .kind()
            .map(|kind| kind.as_str().to_string())
            .unwrap_or_else(|| self.api_provider.as_str().to_string())

View on GitHub (pinned to 0c42157ee5)