{"record":{"id":"6c41d0d3bcae8289","repo":"zeroclaw-labs/zeroclaw","slug":"modelprovider-name-cannot-be-empty","errorCode":null,"errorMessage":"ModelProvider name cannot be empty","messagePattern":"ModelProvider name cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/zeroclaw-providers/src/auth/mod.rs","lineNumber":614,"sourceCode":"#[serde(rename_all = \"kebab-case\")]\npub enum AuthProvider {\n    #[serde(alias = \"openai_codex\", alias = \"codex\")]\n    OpenaiCodex,\n    #[serde(alias = \"claude\")]\n    Anthropic,\n    #[serde(alias = \"google\", alias = \"vertex\")]\n    Gemini,\n    #[serde(alias = \"grok\")]\n    Xai,\n}\n\nimpl std::str::FromStr for AuthProvider {\n    type Err = anyhow::Error;\n\n    fn from_str(raw: &str) -> Result<Self> {\n        let normalized = raw.trim().to_ascii_lowercase();\n        if normalized.is_empty() {\n            anyhow::bail!(\"ModelProvider name cannot be empty\");\n        }\n        serde_json::from_value(serde_json::Value::String(normalized.clone())).map_err(|_| {\n            ::zeroclaw_log::record!(\n                WARN,\n                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)\n                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                    .with_attrs(::serde_json::json!({\"normalized\": &normalized})),\n                \"auth: unknown auth provider\"\n            );\n            anyhow::Error::msg(format!(\n                \"Unknown auth provider `{normalized}`. Supported: openai-codex, anthropic, gemini, xai.\",\n            ))\n        })\n    }\n}\n\nimpl AuthProvider {\n    /// Canonical lowercase name for storage, profile lookup, and on-the-wire","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-providers/src/auth/mod.rs#L596-L632","documentation":"AuthProvider::from_str rejects input that is empty after trim + to_ascii_lowercase. from_str first normalizes, then bails on the empty string before attempting serde deserialization of the canonical provider name (openai-codex, anthropic, gemini, xai and their aliases). A non-empty but unknown name takes a different path (serde error with a WARN log), so this specific error means the caller passed \"\" or whitespace only.","triggerScenarios":"Parsing a model-provider string built from an unset config key or env var (e.g. format!(\"{}\", missing) yielding \"\"), a CLI --model-provider flag left blank, or config.toml with model_provider = \"\".","commonSituations":"A template/config generator that leaves model_provider empty by default, an env var name typo so the value resolves to an empty string, or string interpolation of an Option that renders as empty text.","solutions":["Set a real provider name: openai-codex (alias codex), anthropic (alias claude), gemini (alias google/vertex), or xai (alias grok)","Trace where the empty string comes from — usually an unset config key or environment variable feeding the parse call","Validate provider names early at config load with normalize_model_provider instead of deep inside auth flows"],"exampleFix":"// before: parsing an optional config value directly\nlet provider: AuthProvider = cfg.get(\"model_provider\").unwrap_or(\"\").parse()?;\n\n// after: fail fast on the missing key with a clear message\nlet raw = cfg.get(\"model_provider\").ok_or_else(|| anyhow::anyhow!(\"model_provider not set\"))?;\nlet provider: AuthProvider = raw.parse()?;","handlingStrategy":"validation","validationCode":"fn provider_name_is_parseable(raw: &str) -> bool {\n    !raw.trim().is_empty()\n}\n\nassert!(provider_name_is_parseable(&raw));\nlet provider: AuthProvider = raw.parse()?;","typeGuard":"fn is_non_empty_provider_name(raw: &str) -> bool {\n    !raw.trim().is_empty()\n}","tryCatchPattern":"match raw.parse::<AuthProvider>() {\n    Ok(p) => Ok(p),\n    Err(e) if e.to_string().contains(\"cannot be empty\") => {\n        Err(anyhow::anyhow!(\"model_provider is not configured\"))\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Make model_provider a required config key and validate at startup","Never interpolate optional env/config values into provider names without a default"],"tags":["validation","config","model-provider","empty-string"],"backgroundTag":"empty-string-validation","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}