sigoden/aichat · error · anyhow::Error

No valid models

Error message

No valid models

What it means

fetch_models calls the provider's {api_base}/models endpoint and extracts the 'id' fields from the 'data' array of the JSON response. If the extracted list is empty — because the response has no 'data' array, wrong shape, or zero entries — it throws 'No valid models'.

Solutions

  1. Verify api_base points to an OpenAI-compatible endpoint exposing GET /models
  2. Check that the API key is valid and has access to at least one model; test with curl -H "Authorization: Bearer $KEY" {api_base}/models
  3. Inspect the raw JSON response shape and confirm it contains data[].id strings
  4. Point at a different deployment/provider that actually serves models

Example fix

// before
let models = fetch_models("https://wrong-host.example/v1", Some("sk-bad"))?;
// after
let models = fetch_models("https://api.openai.com/v1", Some("sk-correct"))?;
Defensive patterns

Strategy: validation

Validate before calling

// preflight: call /models yourself and check shape
let v: serde_json::Value = client.get(format!("{base}/models")).send().await?.json().await?;
let ok = v.get("data").and_then(|d| d.as_array()).map(|a| !a.is_empty()).unwrap_or(false);
if !ok { /* fix api_base/api_key before calling fetch_models */ }

Type guard

fn has_models(v: &serde_json::Value) -> bool {
    v.get("data").and_then(|d| d.as_array())
     .map(|a| a.iter().any(|m| m.get("id").and_then(|i| i.as_str()).is_some()))
     .unwrap_or(false)
}

Try / catch

match fetch_models(api_base, api_key).await {
    Ok(models) if !models.is_empty() => models,
    Ok(_) => anyhow::bail!("provider returned empty model list; check api_base/key"),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling fetch_models when the /models response contains no data array of objects with string 'id' fields, or the array is empty.

Common situations: Wrong api_base pointing to a non-OpenAI-compatible server; missing/invalid API key so the server returns an error object instead of a model list; provider version change that renamed the response field; self-hosted gateway that returns an empty model list.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/323b3709251ae959. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/request.rs:189

        Ok(ref client) => client,
        Err(ref err) => bail!("{err}"),
    };
    let mut builder = client.get(format!("{}/models", api_base.trim_end_matches('/')));
    if let Some(api_key) = api_key {
        builder = builder.bearer_auth(api_key);
    }
    let res_body: Value = builder.send().await?.json().await?;
    let mut result: Vec<String> = res_body
        .get("data")
        .and_then(|v| v.as_array())
        .map(|v| {
            v.iter()
                .filter_map(|v| v.get("id").and_then(|v| v.as_str().map(|v| v.to_string())))
                .collect()
        })
        .unwrap_or_default();
    if result.is_empty() {
        bail!("No valid models")
    }
    result.sort_unstable();
    Ok(result)
}

#[derive(Debug, Clone, Default)]
pub struct CrawlOptions {
    extract: Option<String>,
    exclude: Vec<String>,
    no_log: bool,
}

impl CrawlOptions {
    pub fn preset(start_url: &str) -> CrawlOptions {
        for (re, options) in PRESET.iter() {
            if let Ok(true) = re.is_match(start_url) {
                return options.clone();
            }

View on GitHub (pinned to 82976d349a)