{"record":{"id":"1e09c2055313e4cd","repo":"zeroclaw-labs/zeroclaw","slug":"channels-lark-alias-has-use-feishu-false-but-c","errorCode":null,"errorMessage":"[channels.lark.{alias}] has use_feishu=false but cron channel=\"feishu.{alias}\"; use channel=\"lark.{alias}\" or set use_feishu=true","messagePattern":"\\[channels\\.lark\\.(.+?)\\] has use_feishu=false but cron channel=\"feishu\\.(.+?)\"; use channel=\"lark\\.(.+?)\" or set use_feishu=true","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/orchestrator/mod.rs","lineNumber":12941,"sourceCode":"            // cron alias the user wrote.\n            let lk = config.channels.lark.get(alias).ok_or_else(|| {\n                ::zeroclaw_log::record!(\n                    ERROR,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure),\n                    &format!(\n                        \"[channels.lark.{alias}] not configured (cron channel \\\"{channel_type}.{alias}\\\")\"\n                    )\n                );\n                anyhow::Error::msg(format!(\n                    \"[channels.lark.{alias}] not configured (cron channel \\\"{channel_type}.{alias}\\\")\"\n                ))\n            })?;\n            // Asymmetric by design: \"feishu\"+use_feishu=false is a typo\n            // (hard fail). \"lark\"+use_feishu=true is a soft compat path\n            // (warn but still deliver via fallback construction).\n            if channel_type == \"feishu\" && !lk.use_feishu {\n                anyhow::bail!(\n                    \"[channels.lark.{alias}] has use_feishu=false but cron channel=\\\"feishu.{alias}\\\"; \\\n                     use channel=\\\"lark.{alias}\\\" or set use_feishu=true\"\n                );\n            }\n            if channel_type == \"lark\" && lk.use_feishu {\n                ::zeroclaw_log::record!(\n                    WARN,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Note)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Unknown),\n                    &format!(\n                        \"cron channel=\\\"lark.{alias}\\\" with [channels.lark.{alias}] use_feishu=true \\\n                         falls back to one-shot channel construction; prefer channel=\\\"feishu.{alias}\\\" \\\n                         to reuse the live Feishu handle from start_channels\"\n                    )\n                );\n            }\n            let peers = config.channel_external_peers(\"lark\", alias);\n            let peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> =","sourceCodeStart":12923,"sourceCodeEnd":12959,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/orchestrator/mod.rs#L12923-L12959","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If the endpoint really is Lark, change the cron to channel = \"lark.<alias>\"","If the endpoint really is Feishu, set use_feishu = true under [channels.lark.<alias>]","Audit every cron/routing entry that uses the feishu. prefix after any use_feishu change"],"exampleFix":"# before\n[channels.lark.work]\napp_id = \"cli_x\"\nuse_feishu = false\n[cron.report]\nchannel = \"feishu.work\"\n\n# after\n[cron.report]\nchannel = \"lark.work\"   # endpoint is Lark; keep use_feishu = false","handlingStrategy":"validation","validationCode":"// Before scheduling/delivering, enforce the lark/feishu naming rule:\nfn check_lark_ref(config: &Config, channel: &str) -> anyhow::Result<()> {\n    let (kind, alias) = channel.split_once('.').context(\"channel must be <type>.<alias>\")?;\n    if let Some(lk) = config.channels.lark.get(alias) {\n        if kind.eq_ascii_lowercase(\"feishu\") && !lk.use_feishu {\n            anyhow::bail!(\"feishu.{alias} requires use_feishu=true; use lark.{alias} instead\");\n        }\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":"match deliver_announcement(&cfg, \"feishu.work\", &target, thread, &out).await {\n    Err(e) if e.to_string().contains(\"use_feishu=false\") => {\n        // Deterministic config mismatch: rewrite the cron channel or flip use_feishu; do not retry.\n    }\n    rest => rest?,\n}","preventionTips":["Pick one naming convention per deployment and lint crons against it","When flipping use_feishu, grep all cron/routing entries for the other prefix in the same change","Remember the asymmetry: feishu+false hard-fails, lark+true only warns"],"tags":["lark","feishu","config-mismatch","cron-delivery","channels"],"backgroundTag":"config-value-mismatch","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}