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

unsupported delivery mode: {}

Error message

unsupported delivery mode: {}

What it means

validate_delivery_config whitelists cron delivery modes: 'none' (case-insensitive) returns Ok immediately; 'announce' proceeds to require a channel; every other mode string bails. Webhook delivery is NOT a mode — webhook targets are configured under announce mode (validate_delivery_accepts_webhook_with_thread_id pins this), which is why mode='webhook' is rejected.

Source

Thrown at crates/zeroclaw-runtime/src/cron/mod.rs:145

        agent_alias,
        name,
        schedule,
        command,
        delivery,
        shell_output_format,
    )
}

pub fn validate_delivery_config(delivery: Option<&DeliveryConfig>) -> Result<()> {
    let Some(delivery) = delivery else {
        return Ok(());
    };

    if delivery.mode.eq_ignore_ascii_case("none") {
        return Ok(());
    }
    if !delivery.mode.eq_ignore_ascii_case("announce") {
        bail!("unsupported delivery mode: {}", delivery.mode);
    }

    let channel = delivery.channel.as_deref().map(str::trim);
    if channel.filter(|value| !value.is_empty()).is_none() {
        bail!("delivery.channel is required for announce mode");
    }

    let has_target = delivery
        .to
        .as_deref()
        .map(str::trim)
        .is_some_and(|value| !value.is_empty());
    if !has_target {
        bail!("delivery.to is required for announce mode");
    }

    Ok(())
}

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Set delivery.mode to 'announce' (and provide delivery.channel) or 'none'
  2. For webhook delivery, keep mode 'announce' and configure the webhook target/thread id in the delivery target fields
  3. Check the cron config schema/docs for accepted mode and target fields
  4. Normalize the mode string (trim, lowercase) before validation if config sources may inject whitespace or casing

Example fix

// before
delivery = { mode: "webhook", channel: None }

// after
delivery = { mode: "announce", channel: Some("telegram:ops".into()), to: Some(target) }
Defensive patterns

Strategy: validation

Validate before calling

if !delivery_is_valid(&delivery) {
    anyhow::bail!("delivery.mode must be 'none' or 'announce' (webhooks are configured as announce targets)");
}
cron.add_shell_job(spec, delivery).await?;

Type guard

fn is_supported_delivery_mode(mode: &str) -> bool {
    matches!(mode.trim().to_ascii_lowercase().as_str(), "none" | "announce")
}

Prevention

When it happens

Trigger: add_shell_job / add_agent_job / cron handle_command invoked with delivery.mode set to 'webhook', 'send', 'message', 'email', or a typo like 'anounce'. Case variants such as 'Announce' pass; any other token does not.

Common situations: Users assuming webhook is a first-class delivery mode; configs copied from other tools with different vocabularies; config drift across versions where docs/examples disagreed.

Understand the failure class

Background: Config validation failed: what "invalid value for {key}" and settings-rejection errors mean across 19 open-source libraries — this error's family across 19 libraries.

Related errors


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