zeroclaw-labs/zeroclaw · error · anyhow::Error

{} model list failed at {url}: HTTP {status}

Error message

{} model list failed at {url}: HTTP {status}

What it means

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.

Source

Thrown at crates/zeroclaw-providers/src/compatible.rs:2678

                        ERROR,
                        ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                            .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                            .with_attrs(::serde_json::json!({
                                "model_provider": &self.name,
                                "url": &url,
                                "phase": "model_list_request",
                                "error": super::format_error_chain(&e),
                            })),
                        "compatible: model list request failed"
                    );
                    anyhow::Error::msg(format!(
                        "{} model list request failed: {url}: {e}",
                        self.name
                    ))
                })?;
            if !response.status().is_success() {
                let status = response.status();
                anyhow::bail!("{} model list failed at {url}: HTTP {status}", self.name);
            }
            let body: ModelsResponse = response.json().await.map_err(|e| {
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                        .with_attrs(::serde_json::json!({
                            "model_provider": &self.name,
                            "phase": "model_list_parse",
                            "error": super::format_error_chain(&e),
                        })),
                    "compatible: model list returned invalid JSON"
                );
                anyhow::Error::msg(format!(
                    "{} model list returned invalid JSON: {e}",
                    self.name
                ))
            })?;

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Reproduce directly: curl -i {base_url}/models with the same Authorization header and read the status
  2. Fix base_url so it points at the API root that serves /models (usually ends in /v1, no trailing slash)
  3. Verify the API key is valid and unexpired for that provider
  4. If the server truly has no /models, configure a static model list on the alias or enable the models_dev/openrouter catalog fallback

Example fix

# before
[model_provider.local]
family = "compatible"
base_url = "http://localhost:11434"

# after
[model_provider.local]
family = "compatible"
base_url = "http://localhost:11434/v1"
Defensive patterns

Strategy: try-catch

Validate before calling

// Sanity-check the URL shape before trusting it for listing
let url = reqwest::Url::parse(&format!("{base_url}/models"))?;
if !url.scheme().starts_with("http") || base_url.ends_with('/') {
    anyhow::bail!("base_url must not end with '/' and must be a valid http(s) root");
}

Try / catch

match provider.list_models().await {
    Ok(models) => Ok(models),
    Err(e) => {
        let msg = e.to_string();
        if msg.contains("HTTP 429") || msg.contains("HTTP 5") {
            backoff_retry(/* ... */).await // transient: retry with jitter
        } else if msg.contains("HTTP 401") || msg.contains("HTTP 403") {
            /* credential problem: surface to user, do not retry */
            Err(e)
        } else {
            Err(e) // 404 etc: fix base_url / static list
        }
    }
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/49e97424e5b5d0c8. Report an issue: GitHub.