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
- Verify api_base points to an OpenAI-compatible endpoint exposing GET /models
- 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
- Inspect the raw JSON response shape and confirm it contains data[].id strings
- 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
- Verify api_base with curl -H "Authorization: Bearer $KEY" {base}/models before wiring it up
- Confirm the server is OpenAI-compatible and returns data[].id
- Use a scoped key with model-list permissions
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)