{"record":{"id":"82a5481b0af85473","repo":"zeroclaw-labs/zeroclaw","slug":"fallback-provider-raw-resolved-to-resolved","errorCode":null,"errorMessage":"Fallback provider `{raw}` resolved to `{resolved}` ({profile}) but no profile-resolved credential exists. Set `api_key` on {profile}, configure the alias's external auth flow, or remove it from `fallback`.","messagePattern":"Fallback provider `(.+?)` resolved to `(.+?)` \\((.+?)\\) but no profile-resolved credential exists\\. Set `api_key` on (.+?), configure the alias's external auth flow, or remove it from `fallback`\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-providers/src/lib.rs","lineNumber":1564,"sourceCode":"                WARN,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Unknown)\n                    .with_attrs(::serde_json::json!({\"fallback\": resolved})),\n                \"fallback ref closes a cycle; pruning\"\n            );\n            continue;\n        }\n\n        let opts = provider_runtime_options_for_alias(config, family, &alias);\n        if !factory::fallback_auth_ready_for_alias(\n            config,\n            family,\n            &alias,\n            entry.api_key.as_deref(),\n            &opts,\n        ) {\n            let profile = format!(\"[providers.models.{family}.{alias}]\");\n            anyhow::bail!(\n                \"Fallback provider `{raw}` resolved to `{resolved}` ({profile}) but no \\\n                 profile-resolved credential exists. Set `api_key` on {profile}, configure \\\n                 the alias's external auth flow, or remove it from `fallback`.\"\n            );\n        }\n\n        match create_model_provider_inner(\n            Some(config),\n            family,\n            &alias,\n            entry.api_key.as_deref(),\n            entry.uri.as_deref(),\n            &opts,\n        ) {\n            Ok(built) => push_pinned_entries(out, config, family, &alias, built, None),\n            Err(e) => {\n                let profile = format!(\"[providers.models.{family}.{alias}]\");\n                anyhow::bail!(","sourceCodeStart":1546,"sourceCodeEnd":1582,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/lib.rs#L1546-L1582","documentation":"While assembling the reliability chain, append_fallback_chain resolves each `fallback` reference to a typed alias and calls fallback_auth_ready_for_alias. A reference that resolves but whose alias has no api_key and no configured external auth flow is a hard startup error naming the exact profile — deliberately stricter than an unresolvable reference, which is only skipped with a warning. This guarantees a declared fallback is never silently credential-less.","triggerScenarios":"`fallback = [\"openai.backup\"]` where `[providers.models.openai.backup]` exists but has no api_key and no resolvable env credential; an OAuth-backed alias (qwen/minimax) whose login was never performed.","commonSituations":"Adding fallbacks by copying alias names without copying credentials; per-provider env vars present locally but missing in CI; relying on an external auth flow that has not run on the new host.","solutions":["Set `api_key` on the named profile `[providers.models.<family>.<alias>]`","Complete the alias's external auth flow (e.g. `qwen login`, MiniMax token) if the family uses OAuth","Remove the entry from `fallback` if it should not be a failover target","Point the fallback at a different alias that is fully configured"],"exampleFix":"# before\n[providers.models.openai.primary]\nfallback = [\"openai.backup\"]\n\n[providers.models.openai.backup]\nmodel = \"gpt-4o-mini\"\n\n# after\n[providers.models.openai.primary]\nfallback = [\"openai.backup\"]\n\n[providers.models.openai.backup]\nmodel = \"gpt-4o-mini\"\napi_key = \"sk-...\"","handlingStrategy":"validation","validationCode":"fn fallbacks_have_credentials(\n    config: &zeroclaw_config::schema::Config,\n    fallbacks: &[String],\n) -> Result<(), String> {\n    for raw in fallbacks {\n        let Some((family, alias, entry)) = config.providers.models.find_by_name(raw) else { continue };\n        let has_key = entry.api_key.as_deref().map(|k| !k.trim().is_empty()).unwrap_or(false);\n        if !has_key {\n            return Err(format!(\"fallback {family}.{alias} has no api_key or auth flow\"));\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = build_resilient(config, &name).await {\n    if e.to_string().contains(\"no profile-resolved credential exists\") {\n        // startup config error: surface the named profile to the operator, do not retry\n        return Err(e.context(\"configure fallback credentials or drop the fallback entry\"));\n    }\n    return Err(e);\n}","preventionTips":["Add fallback entries only for aliases that already hold credentials in the same environment","Include fallback credentials in CI secret provisioning","Run OAuth logins for OAuth-backed fallback aliases on every host"],"tags":["fallback","credentials","configuration","startup"],"backgroundTag":"missing-provider-credentials","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}