{"record":{"id":"e82cf17b2d148619","repo":"zeroclaw-labs/zeroclaw","slug":"model-provider-trimmed-must-use-type-alias","errorCode":null,"errorMessage":"model_provider `{trimmed}` must use `<type>.<alias>` form","messagePattern":"model_provider `(.+?)` must use `<type>\\.<alias>` form","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/orchestrator/mod.rs","lineNumber":1626,"sourceCode":"        anyhow::bail!(\n            \"agents.{agent_alias}.model_provider is empty; runtime reload requires a dotted `<type>.<alias>` provider reference\"\n        );\n    }\n    let (model_provider, _) = model_provider_entry_for_ref(config, configured)?;\n    Ok(model_provider)\n}\n\nfn model_provider_entry_for_ref<'a>(\n    config: &'a Config,\n    model_provider: &str,\n) -> anyhow::Result<(String, &'a zeroclaw_config::schema::ModelProviderConfig)> {\n    let trimmed = model_provider.trim();\n    if trimmed.is_empty() {\n        anyhow::bail!(\"model_provider reference must not be empty\");\n    }\n\n    let Some((provider_type, provider_alias)) = trimmed.split_once('.') else {\n        anyhow::bail!(\"model_provider `{trimmed}` must use `<type>.<alias>` form\");\n    };\n    let Some(entry) = config.providers.models.find(provider_type, provider_alias) else {\n        anyhow::bail!(\"model_provider `{trimmed}` does not resolve to a configured provider\");\n    };\n    Ok((trimmed.to_string(), entry))\n}\n\n/// Resolve runtime defaults from `config` against a specific dotted\n/// `model_provider` reference (`\"<type>.<alias>\"`) — the per-agent\n/// resolution path.\nfn runtime_defaults_from_config(\n    config: &Config,\n    model_provider: &str,\n) -> anyhow::Result<ChannelRuntimeDefaults> {\n    let (default_model_provider, entry) = model_provider_entry_for_ref(config, model_provider)?;\n    let model = entry\n        .model\n        .as_deref()","sourceCodeStart":1608,"sourceCodeEnd":1644,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/orchestrator/mod.rs#L1608-L1644","documentation":"Thrown by model_provider_entry_for_ref when the trimmed provider reference contains no '.' separator, so `split_once('.')` fails. ZeroClaw model providers are addressed as `<type>.<alias>` (e.g. `openai.default`) because each family can hold multiple credentialed aliases under `[providers.models.<type>.<alias>]`; a bare name like `openai` is ambiguous at this layer and is rejected before lookup.","triggerScenarios":"Calling model_provider_entry_for_ref (or the runtime reload path that wraps it) with values like `openai`, `anthropic`, or `my provider` — anything without a dot. Note the `/models` chat command tolerates bare family names via resolve_models_command; this stricter dotted-only contract applies to config-file references and programmatic callers.","commonSituations":"Writing `model_provider = \"openai\"` in an agent stanza copied from older docs or another tool's style; assuming a default alias is implied; using a colon or slash separator instead of a dot.","solutions":["Change the reference to dotted form: `openai` -> `openai.default` (using an alias that exists in config)","Confirm the alias under `[providers.models.<type>]` in zeroclaw.toml and match it exactly after the dot","For `/models` chat commands the bare family form is fine — only config fields and programmatic refs require the dot"],"exampleFix":"# before\n[agents.main]\nmodel_provider = \"openai\"\n# error: model_provider `openai` must use `<type>.<alias>` form\n\n# after\n[agents.main]\nmodel_provider = \"openai.default\"","handlingStrategy":"validation","validationCode":"fn is_dotted_provider_ref(s: &str) -> bool {\n    let t = s.trim();\n    !t.is_empty() && t.split_once('.').is_some_and(|(a, b)| !a.is_empty() && !b.is_empty())\n}\nassert!(is_dotted_provider_ref(&agent.model_provider), \"must be `<type>.<alias>`\");","typeGuard":"fn is_dotted_provider_ref(s: &str) -> bool {\n    let t = s.trim();\n    !t.is_empty()\n        && t.split_once('.')\n            .is_some_and(|(ty, alias)| !ty.is_empty() && !alias.is_empty() && !alias.contains('.'))\n}","tryCatchPattern":"Err(err) if err.to_string().contains(\"must use `<type>.<alias>` form\") => {\n    // rewrite bare family names to `<family>.default` when that alias exists, else reject\n}","preventionTips":["Encode the dotted form in config schemas/docs examples from day one","Validate every agent.model_provider with the is_dotted_provider_ref guard at config load","Reject the value at write time (`zeroclaw config set`) rather than at runtime reload"],"tags":["model-provider","reference-format","configuration","zeroclaw"],"backgroundTag":"malformed-config-reference","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}