{"record":{"id":"83c59eedce3eea31","repo":"zeroclaw-labs/zeroclaw","slug":"model-requested-cloud-routing-but-no-api-key","errorCode":null,"errorMessage":"Model '{}' requested cloud routing, but no API key is configured. Set api_key on [providers.models.ollama.<alias>] or via the schema-mirror grammar.","messagePattern":"Model '(.+?)' requested cloud routing, but no API key is configured\\. Set api_key on \\[providers\\.models\\.ollama\\.<alias>\\] or via the schema-mirror grammar\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/ollama.rs","lineNumber":326,"sourceCode":"    fn resolve_request_details(&self, model: &str) -> anyhow::Result<(String, bool)> {\n        let requests_cloud = model.ends_with(\":cloud\");\n        let official_cloud_endpoint = self.is_official_cloud_endpoint();\n        let local_endpoint = self.is_local_endpoint();\n        let normalized_model = if requests_cloud && official_cloud_endpoint {\n            model.strip_suffix(\":cloud\").unwrap_or(model).to_string()\n        } else {\n            model.to_string()\n        };\n\n        if requests_cloud && local_endpoint {\n            anyhow::bail!(\n                \"Model '{}' requested cloud routing, but Ollama endpoint is local. Configure api_url with a remote Ollama endpoint.\",\n                model\n            );\n        }\n\n        if requests_cloud && official_cloud_endpoint && self.api_key.is_none() {\n            anyhow::bail!(\n                \"Model '{}' requested cloud routing, but no API key is configured. Set api_key on [providers.models.ollama.<alias>] or via the schema-mirror grammar.\",\n                model\n            );\n        }\n\n        let should_auth = self.api_key.is_some() && !local_endpoint;\n\n        Ok((normalized_model, should_auth))\n    }\n\n    fn parse_tool_arguments(arguments: &str) -> serde_json::Value {\n        serde_json::from_str(arguments).unwrap_or_else(|_| serde_json::json!({}))\n    }\n\n    fn normalize_response_text(content: String) -> Option<String> {\n        let stripped = Self::strip_think_tags(&content);\n        if stripped.trim().is_empty() {\n            None","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/ollama.rs#L308-L344","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Set `api_key` on the ollama alias profile used for cloud models","If authenticating through the schema-mirror grammar, make sure the key lands on that same alias","If cloud was not intended, remove the `:cloud` suffix or point api_url back at your local server"],"exampleFix":"# before\n[providers.models.ollama.cloud]\napi_url = \"https://ollama.com\"\nmodel = \"llama3.1:cloud\"\n\n# after\n[providers.models.ollama.cloud]\napi_url = \"https://ollama.com\"\napi_key = \"sk-ollama-...\"\nmodel = \"llama3.1:cloud\"","handlingStrategy":"validation","validationCode":"fn ollama_cloud_auth_ready(model: &str, api_url: &str, api_key: Option<&str>) -> bool {\n    if !model.ends_with(\":cloud\") { return true; }\n    let official = reqwest::Url::parse(api_url)\n        .ok()\n        .and_then(|u| u.host_str().map(|h| h.eq_ignore_ascii_case(\"ollama.com\") || h.eq_ignore_ascii_case(\"api.ollama.com\")))\n        .unwrap_or(false);\n    !official || api_key.map(|k| !k.trim().is_empty()).unwrap_or(false)\n}","typeGuard":"fn needs_ollama_cloud_key(model: &str, api_url: &str) -> bool {\n    model.ends_with(\":cloud\") && {\n        let host = reqwest::Url::parse(api_url).ok().and_then(|u| u.host_str().map(|h| h.to_string()));\n        host.map(|h| h.eq_ignore_ascii_case(\"ollama.com\") || h.eq_ignore_ascii_case(\"api.ollama.com\")).unwrap_or(false)\n    }\n}","tryCatchPattern":"if needs_ollama_cloud_key(model, api_url) && api_key_is_none() {\n    return Err(anyhow::anyhow!(\"set api_key on the ollama alias for cloud routing\"));\n}\nollama.chat(req, model, temp).await","preventionTips":["Configure the cloud ollama alias with its api_key at creation time","Keep cloud and local aliases separate so the key is never assumed","Alert on empty-string api_key overrides in automation"],"tags":["ollama","cloud-routing","api-key","authentication"],"backgroundTag":"missing-api-key","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}