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

Model '{}' requested cloud routing, but no API key is config

Error message

Model '{}' requested cloud routing, but no API key is configured. Set api_key on [providers.models.ollama.<alias>] or via the schema-mirror grammar.

What it means

When a model requests `:cloud` routing and the endpoint host is ollama.com or api.ollama.com (the official cloud), an API key is mandatory; with none configured on the alias, the request fails pre-flight. Once a key exists, should_auth becomes true and it is sent as a bearer credential to the remote endpoint.

Source

Thrown at crates/zeroclaw-providers/src/ollama.rs:326

    fn resolve_request_details(&self, model: &str) -> anyhow::Result<(String, bool)> {
        let requests_cloud = model.ends_with(":cloud");
        let official_cloud_endpoint = self.is_official_cloud_endpoint();
        let local_endpoint = self.is_local_endpoint();
        let normalized_model = if requests_cloud && official_cloud_endpoint {
            model.strip_suffix(":cloud").unwrap_or(model).to_string()
        } else {
            model.to_string()
        };

        if requests_cloud && local_endpoint {
            anyhow::bail!(
                "Model '{}' requested cloud routing, but Ollama endpoint is local. Configure api_url with a remote Ollama endpoint.",
                model
            );
        }

        if requests_cloud && official_cloud_endpoint && self.api_key.is_none() {
            anyhow::bail!(
                "Model '{}' requested cloud routing, but no API key is configured. Set api_key on [providers.models.ollama.<alias>] or via the schema-mirror grammar.",
                model
            );
        }

        let should_auth = self.api_key.is_some() && !local_endpoint;

        Ok((normalized_model, should_auth))
    }

    fn parse_tool_arguments(arguments: &str) -> serde_json::Value {
        serde_json::from_str(arguments).unwrap_or_else(|_| serde_json::json!({}))
    }

    fn normalize_response_text(content: String) -> Option<String> {
        let stripped = Self::strip_think_tags(&content);
        if stripped.trim().is_empty() {
            None

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set `api_key` on the ollama alias profile used for cloud models
  2. If authenticating through the schema-mirror grammar, make sure the key lands on that same alias
  3. If cloud was not intended, remove the `:cloud` suffix or point api_url back at your local server

Example fix

# before
[providers.models.ollama.cloud]
api_url = "https://ollama.com"
model = "llama3.1:cloud"

# after
[providers.models.ollama.cloud]
api_url = "https://ollama.com"
api_key = "sk-ollama-..."
model = "llama3.1:cloud"
Defensive patterns

Strategy: validation

Validate before calling

fn ollama_cloud_auth_ready(model: &str, api_url: &str, api_key: Option<&str>) -> bool {
    if !model.ends_with(":cloud") { return true; }
    let official = reqwest::Url::parse(api_url)
        .ok()
        .and_then(|u| u.host_str().map(|h| h.eq_ignore_ascii_case("ollama.com") || h.eq_ignore_ascii_case("api.ollama.com")))
        .unwrap_or(false);
    !official || api_key.map(|k| !k.trim().is_empty()).unwrap_or(false)
}

Type guard

fn needs_ollama_cloud_key(model: &str, api_url: &str) -> bool {
    model.ends_with(":cloud") && {
        let host = reqwest::Url::parse(api_url).ok().and_then(|u| u.host_str().map(|h| h.to_string()));
        host.map(|h| h.eq_ignore_ascii_case("ollama.com") || h.eq_ignore_ascii_case("api.ollama.com")).unwrap_or(false)
    }
}

Try / catch

if needs_ollama_cloud_key(model, api_url) && api_key_is_none() {
    return Err(anyhow::anyhow!("set api_key on the ollama alias for cloud routing"));
}
ollama.chat(req, model, temp).await

Prevention

When it happens

Trigger: model ending in `:cloud`, api_url pointing at ollama.com or api.ollama.com, and the corresponding `[providers.models.ollama.<alias>]` has no api_key (and none supplied via the schema-mirror grammar).

Common situations: New Ollama-cloud setups missing the key; the key configured on a different alias than the one the model resolves to; automation overriding api_key with an empty string; local and cloud aliases mixed and the cloud one left bare.

Understand the failure class

Background: "API key is required" / "API key not found" / "No API key was set": the missing-api-key error family across 16 libraries — this error's family across 16 libraries.

Related errors


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