zeroclaw-labs/zeroclaw · error

[channels.lark.{alias}] has use_feishu=false but cron channe

Error message

[channels.lark.{alias}] has use_feishu=false but cron channel="feishu.{alias}"; use channel="lark.{alias}" or set use_feishu=true

What it means

Both "lark.<alias>" and "feishu.<alias>" resolve through the single [channels.lark.<alias>] config table (the single source of truth for both names). This bail fires when the cron channel says feishu but that table has use_feishu = false: that combination is treated as a typo and hard-fails by design. It is asymmetric on purpose — "lark" with use_feishu = true only logs a warning and still delivers via fallback construction.

Source

Thrown at crates/zeroclaw-channels/src/orchestrator/mod.rs:12941

            // cron alias the user wrote.
            let lk = config.channels.lark.get(alias).ok_or_else(|| {
                ::zeroclaw_log::record!(
                    ERROR,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                        .with_outcome(::zeroclaw_log::EventOutcome::Failure),
                    &format!(
                        "[channels.lark.{alias}] not configured (cron channel \"{channel_type}.{alias}\")"
                    )
                );
                anyhow::Error::msg(format!(
                    "[channels.lark.{alias}] not configured (cron channel \"{channel_type}.{alias}\")"
                ))
            })?;
            // Asymmetric by design: "feishu"+use_feishu=false is a typo
            // (hard fail). "lark"+use_feishu=true is a soft compat path
            // (warn but still deliver via fallback construction).
            if channel_type == "feishu" && !lk.use_feishu {
                anyhow::bail!(
                    "[channels.lark.{alias}] has use_feishu=false but cron channel=\"feishu.{alias}\"; \
                     use channel=\"lark.{alias}\" or set use_feishu=true"
                );
            }
            if channel_type == "lark" && lk.use_feishu {
                ::zeroclaw_log::record!(
                    WARN,
                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)
                        .with_outcome(::zeroclaw_log::EventOutcome::Unknown),
                    &format!(
                        "cron channel=\"lark.{alias}\" with [channels.lark.{alias}] use_feishu=true \
                         falls back to one-shot channel construction; prefer channel=\"feishu.{alias}\" \
                         to reuse the live Feishu handle from start_channels"
                    )
                );
            }
            let peers = config.channel_external_peers("lark", alias);
            let peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> =

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. If the endpoint really is Lark, change the cron to channel = "lark.<alias>"
  2. If the endpoint really is Feishu, set use_feishu = true under [channels.lark.<alias>]
  3. Audit every cron/routing entry that uses the feishu. prefix after any use_feishu change

Example fix

# before
[channels.lark.work]
app_id = "cli_x"
use_feishu = false
[cron.report]
channel = "feishu.work"

# after
[cron.report]
channel = "lark.work"   # endpoint is Lark; keep use_feishu = false
Defensive patterns

Strategy: validation

Validate before calling

// Before scheduling/delivering, enforce the lark/feishu naming rule:
fn check_lark_ref(config: &Config, channel: &str) -> anyhow::Result<()> {
    let (kind, alias) = channel.split_once('.').context("channel must be <type>.<alias>")?;
    if let Some(lk) = config.channels.lark.get(alias) {
        if kind.eq_ascii_lowercase("feishu") && !lk.use_feishu {
            anyhow::bail!("feishu.{alias} requires use_feishu=true; use lark.{alias} instead");
        }
    }
    Ok(())
}

Try / catch

match deliver_announcement(&cfg, "feishu.work", &target, thread, &out).await {
    Err(e) if e.to_string().contains("use_feishu=false") => {
        // Deterministic config mismatch: rewrite the cron channel or flip use_feishu; do not retry.
    }
    rest => rest?,
}

Prevention

When it happens

Trigger: Cron channel = "feishu.work" while [channels.lark.work] has use_feishu = false (or unset when false is the default); deliver_announcement hits the mismatch check right after resolving the config table.

Common situations: Copy-pasting a channel ref from a Feishu-based example into a Lark-endpoint deployment (or vice versa); flipping use_feishu after crons were already written with the feishu. prefix; team confusion because both names target the same config table.

Related errors


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