zeroclaw-labs/zeroclaw · error · anyhow::Error

agents.{agent_alias}.model_provider is empty — set it to a c

Error message

agents.{agent_alias}.model_provider is empty — set it to a configured "<type>.<alias>" (e.g. "anthropic.{agent_alias}")

What it means

The process_message twin of the empty model_provider error: on a non-interactive turn, the agent's model_provider is an empty string, which the V3 schema treats as a config error rather than falling back to a default provider. Every agent must name its provider explicitly.

Source

Thrown at crates/zeroclaw-runtime/src/agent/loop_.rs:2913

        let observer: Arc<dyn Observer> =
            Arc::from(observability::create_observer(&config.observability));
        let runtime: Arc<dyn platform::RuntimeAdapter> =
            Arc::from(platform::create_runtime(&config.runtime)?);
        let security = Arc::new(SecurityPolicy::for_agent(&config, agent_alias)?);
        let (provider_name, provider_alias, agent_model_provider) = match config
            .resolved_model_provider_for_agent(agent_alias)
        {
            Some(resolved) => (resolved.0, resolved.1.to_string(), Some(resolved.2.clone())),
            None => {
                let agent_ref = agent.model_provider.as_str();
                if !agent_ref.is_empty() {
                    anyhow::bail!(
                        "agents.{agent_alias}.model_provider = \"{agent_ref}\" does not resolve to \
                     a configured [providers.models.<type>.<alias>] entry"
                    );
                }
                anyhow::bail!(
                    "agents.{agent_alias}.model_provider is empty \u{2014} set it to a configured \
                 \"<type>.<alias>\" (e.g. \"anthropic.{agent_alias}\")"
                );
            }
        };
        let approval_manager = ApprovalManager::for_non_interactive(&risk_profile);
        let mem: Arc<dyn Memory> = zeroclaw_memory::create_memory_for_agent(
            &config,
            agent_alias,
            agent_model_provider
                .as_ref()
                .and_then(|e| e.api_key.as_deref()),
        )
        .await?;

        let (composio_key, composio_entity_id) = if config.composio.enabled {
            (
                config.composio.api_key.as_deref(),

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set model_provider = "<type>.<alias>" on the failing agent (e.g. "anthropic.default").
  2. Confirm the referenced provider table exists and sets a model.
  3. Lint all agent tables for non-empty model_provider before reload.

Example fix

# before
[agents.telegram-bot]
model_provider = ""

# after
[agents.telegram-bot]
model_provider = "anthropic.default"
Defensive patterns

Strategy: validation

Validate before calling

// Lint every agent table before enabling channels/cron
for (alias, agent) in &config.agents {
    anyhow::ensure!(
        !agent.model_provider.as_str().trim().is_empty(),
        "agents.{alias}.model_provider is empty"
    );
}

Prevention

When it happens

Trigger: A channel/cron/gateway turn hits an agent whose model_provider was never set or was blanked by a config edit; provisioning generated an agent table with an empty field.

Common situations: Adding agents for channel handlers without wiring model_provider; config overlays clearing the field; migrating older configs where the field was optional.

Related errors


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/eb520cb0eb48946c. Report an issue: GitHub.