zeroclaw-labs/zeroclaw · warning
ModelProvider name cannot be empty
Error message
ModelProvider name cannot be empty
What it means
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.
Source
Thrown at crates/zeroclaw-providers/src/auth/mod.rs:614
#[serde(rename_all = "kebab-case")]
pub enum AuthProvider {
#[serde(alias = "openai_codex", alias = "codex")]
OpenaiCodex,
#[serde(alias = "claude")]
Anthropic,
#[serde(alias = "google", alias = "vertex")]
Gemini,
#[serde(alias = "grok")]
Xai,
}
impl std::str::FromStr for AuthProvider {
type Err = anyhow::Error;
fn from_str(raw: &str) -> Result<Self> {
let normalized = raw.trim().to_ascii_lowercase();
if normalized.is_empty() {
anyhow::bail!("ModelProvider name cannot be empty");
}
serde_json::from_value(serde_json::Value::String(normalized.clone())).map_err(|_| {
::zeroclaw_log::record!(
WARN,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Reject)
.with_outcome(::zeroclaw_log::EventOutcome::Failure)
.with_attrs(::serde_json::json!({"normalized": &normalized})),
"auth: unknown auth provider"
);
anyhow::Error::msg(format!(
"Unknown auth provider `{normalized}`. Supported: openai-codex, anthropic, gemini, xai.",
))
})
}
}
impl AuthProvider {
/// Canonical lowercase name for storage, profile lookup, and on-the-wireView on GitHub (pinned to 88bb9c8533)
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
Example fix
// before: parsing an optional config value directly
let provider: AuthProvider = cfg.get("model_provider").unwrap_or("").parse()?;
// after: fail fast on the missing key with a clear message
let raw = cfg.get("model_provider").ok_or_else(|| anyhow::anyhow!("model_provider not set"))?;
let provider: AuthProvider = raw.parse()?; Defensive patterns
Strategy: validation
Validate before calling
fn provider_name_is_parseable(raw: &str) -> bool {
!raw.trim().is_empty()
}
assert!(provider_name_is_parseable(&raw));
let provider: AuthProvider = raw.parse()?; Type guard
fn is_non_empty_provider_name(raw: &str) -> bool {
!raw.trim().is_empty()
} Try / catch
match raw.parse::<AuthProvider>() {
Ok(p) => Ok(p),
Err(e) if e.to_string().contains("cannot be empty") => {
Err(anyhow::anyhow!("model_provider is not configured"))
}
Err(e) => Err(e),
} Prevention
- Make model_provider a required config key and validate at startup
- Never interpolate optional env/config values into provider names without a default
When it happens
Trigger: 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 = "".
Common situations: 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.
Related errors
- agents.{agent_alias}.model_provider = "{agent_ref}" does not
- agents.{agent_alias}.model_provider is empty — set it to a c
- agents.{agent_alias}.model_provider resolves to a model_prov
- agents.{agent_alias}.model_provider = "{agent_ref}" does not
- matrix: `homeserver` is required
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/6c41d0d3bcae8289.
Report an issue: GitHub.