{"record":{"id":"bd6c9ea635177e66","repo":"zeroclaw-labs/zeroclaw","slug":"providers-models-ollama-alias-model-uses-cloud-bd6c9e","errorCode":null,"errorMessage":"providers.models.ollama.{alias}.model uses ':cloud', but no API key is configured. Set api_key on [providers.models.ollama.{alias}] (or via the schema-mirror grammar: ZEROCLAW_providers__models__ollama__{alias}__api_key=<value>).","messagePattern":"providers\\.models\\.ollama\\.(.+?)\\.model uses ':cloud', but no API key is configured\\. Set api_key on \\[providers\\.models\\.ollama\\.(.+?)\\] \\(or via the schema-mirror grammar: ZEROCLAW_providers__models__ollama__(.+?)__api_key=<value>\\)\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-config/src/schema.rs","lineNumber":21678,"sourceCode":"        for (alias, cfg) in &self.providers.models.ollama {\n            let entry = &cfg.base;\n            if !entry\n                .model\n                .as_deref()\n                .is_some_and(|model| model.trim().ends_with(\":cloud\"))\n            {\n                continue;\n            }\n\n            if is_local_ollama_endpoint(entry.uri.as_deref()) {\n                anyhow::bail!(\n                    \"providers.models.ollama.{alias}.model uses ':cloud', but uri is local or unset. Set uri to a remote Ollama endpoint (for example https://ollama.com).\"\n                );\n            }\n            if is_official_ollama_cloud_endpoint(entry.uri.as_deref())\n                && !has_ollama_cloud_credential(entry.api_key.as_deref())\n            {\n                anyhow::bail!(\n                    \"providers.models.ollama.{alias}.model uses ':cloud', but no API key is configured. Set api_key on [providers.models.ollama.{alias}] (or via the schema-mirror grammar: ZEROCLAW_providers__models__ollama__{alias}__api_key=<value>).\"\n                );\n            }\n        }\n\n        // Microsoft 365\n        if self.microsoft365.enabled {\n            let tenant = self\n                .microsoft365\n                .tenant_id\n                .as_deref()\n                .map(str::trim)\n                .filter(|s| !s.is_empty());\n            if tenant.is_none() {\n                anyhow::bail!(\n                    \"microsoft365.tenant_id must not be empty when microsoft365 is enabled\"\n                );\n            }","sourceCodeStart":21660,"sourceCodeEnd":21696,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-config/src/schema.rs#L21660-L21696","documentation":"When a ':cloud' model's uri host is exactly ollama.com or api.ollama.com (is_official_ollama_cloud_endpoint, schema.rs:19374) and the entry's api_key is None or whitespace-only (has_ollama_cloud_credential, schema.rs:19390), validation fails. The message names both the TOML fix and the schema-mirror env grammar ZEROCLAW_providers__models__ollama__{alias}__api_key as alternative injection points. Keys configured on other aliases do not count.","triggerScenarios":"Configure `uri = \"https://ollama.com\"` with a ':cloud' model but api_key omitted, set to \"\", or whitespace-only; or provide the key via env var under a differently-spelled alias path.","commonSituations":"Keeping secrets out of the committed config but forgetting to export the env var in the deployment unit (systemd, container, CI shell); rotating keys and leaving the old alias empty; typos in the double-underscore env mirror path.","solutions":["Set api_key directly on the failing alias: `api_key = \"...\"` under [providers.models.ollama.<alias>]","Or export ZEROCLAW_providers__models__ollama__<alias>__api_key=<value> in the process environment (alias spelled verbatim, double underscores between path segments)","Verify the value is non-empty after trimming — whitespace-only still fails","Confirm the alias in the message matches where you put the key"],"exampleFix":"# before\n[providers.models.ollama.default]\nmodel = \"gpt-oss:cloud\"\nuri = \"https://ollama.com\"\n\n# after\n[providers.models.ollama.default]\nmodel = \"gpt-oss:cloud\"\nuri = \"https://ollama.com\"\napi_key = \"<ollama-cloud-key>\"\n\n# or, keep the file clean and export:\n# ZEROCLAW_providers__models__ollama__default__api_key=<ollama-cloud-key>","handlingStrategy":"try-catch","validationCode":"fn ollama_key_precheck(cfg: &zeroclaw_config::Config) -> Result<(), String> {\n    for (alias, entry) in &cfg.providers.models.ollama {\n        let cloud = entry.base.model.as_deref().is_some_and(|m| m.trim().ends_with(\":cloud\"));\n        let official = entry.base.uri.as_deref().is_some_and(|u| {\n            reqwest::Url::parse(u.trim()).ok()\n                .and_then(|p| p.host_str().map(|h| h.eq_ignore_ascii_case(\"ollama.com\") || h.eq_ignore_ascii_case(\"api.ollama.com\")))\n                .unwrap_or(false)\n        });\n        if cloud && official && entry.base.api_key.as_deref().map(str::trim).is_none_or(str::is_empty) {\n            return Err(format!(\"alias {alias}: :cloud on official endpoint without api_key\"));\n        }\n    }\n    Ok(())\n}","typeGuard":"fn cloud_entry_has_credential(api_key: Option<&str>) -> bool {\n    api_key.map(str::trim).is_some_and(|v| !v.is_empty)\n}","tryCatchPattern":"if let Err(err) = config.validate() {\n    if err.to_string().contains(\"no API key is configured\") {\n        // export ZEROCLAW_providers__models__ollama__<alias>__api_key or set api_key in TOML, then reload\n    }\n}","preventionTips":["Wire the schema-mirror env var into the service unit template so it can never be forgotten","Fail fast at deploy time if a :cloud alias exists but the env var is unset","Trim-verify keys after rotation; whitespace-only values still fail"],"tags":["configuration","validation","ollama","api-key","providers","zeroclaw","rust"],"backgroundTag":"missing-api-key","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}