{"record":{"id":"6b6f0b9bb4bcd870","repo":"zeroclaw-labs/zeroclaw","slug":"whatsapp-channel-send-requires-web-mode-set-sessi","errorCode":null,"errorMessage":"WhatsApp channel send requires Web mode (set session_path, pair_phone, or mode = personal)","messagePattern":"WhatsApp channel send requires Web mode \\(set session_path, pair_phone, or mode = personal\\)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/orchestrator/mod.rs","lineNumber":9126,"sourceCode":"                        .with_workspace_dir(workspace_dir)\n                        .with_ack_reactions(ack),\n                ))\n            }\n            #[cfg(not(feature = \"channel-matrix\"))]\n            {\n                anyhow::bail!(\"Matrix channel requires the `channel-matrix` feature\");\n            }\n        }\n        \"whatsapp\" | \"whatsapp-web\" | \"whatsapp_web\" => {\n            #[cfg(feature = \"whatsapp-web\")]\n            {\n                let wa = config\n                    .channels\n                    .whatsapp\n                    .get(\"default\")\n                    .context(\"WhatsApp channel is not configured\")?;\n                if !wa.is_web_config() {\n                    anyhow::bail!(\n                        \"WhatsApp channel send requires Web mode (set session_path, pair_phone, or mode = personal)\"\n                    );\n                }\n                let alias = \"default\".to_string();\n                let peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> = {\n                    let cfg_arc = config_arc.clone();\n                    let alias = alias.clone();\n                    Arc::new(move || cfg_arc.read().channel_external_peers(\"whatsapp\", &alias))\n                };\n                let allowed_groups_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> = {\n                    let cfg_arc = config_arc.clone();\n                    let alias = alias.clone();\n                    Arc::new(move || {\n                        cfg_arc\n                            .read()\n                            .channels\n                            .whatsapp\n                            .get(&alias)","sourceCodeStart":9108,"sourceCodeEnd":9144,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/orchestrator/mod.rs#L9108-L9144","documentation":"Thrown by build_channel_by_id for whatsapp/whatsapp-web targets when the `whatsapp-web` feature IS compiled in, the `[channels.whatsapp.default]` section exists, but is_web_config() is false. is_web_config() requires a web-mode selector: session_path, pair_phone, pair_code, ws_url, or mode = Personal. A config that only carries Cloud API fields (phone_number_id, access_token, verify_token) — or is essentially empty — fails this check because the send path only supports Web mode.","triggerScenarios":"Sending via a `whatsapp.*` target when `[channels.whatsapp.default]` is configured for the Cloud API (phone_number_id/access_token/verify_token set, no web selectors), or the section exists but none of session_path/pair_phone/pair_code/ws_url/mode=Personal are set. Note this arm always reads alias \"default\" — other aliases are not consulted here.","commonSituations":"Config written for WhatsApp Cloud API then used with a send path that only supports Web mode; partial web config (e.g. intended to set session_path but left it out); mode left at its Business default where Personal was meant; copy-pasted example config without the web fields.","solutions":["Add a web-mode selector to `[channels.whatsapp.default]`: set `session_path`, `pair_phone`, or `mode = \"personal\"` (pair_code/ws_url also work)","If you meant Cloud API, use the cloud-oriented send/webhook path instead of this one-shot web send","Re-run the send after saving config; the web client defaults a missing session_path once intent is declared"],"exampleFix":"# before (zeroclaw.toml)\n[channels.whatsapp.default]\nphone_number_id = \"123\"\naccess_token = \"EAAB...\"\n# error: WhatsApp channel send requires Web mode\n\n# after\n[channels.whatsapp.default]\nmode = \"personal\"\nsession_path = \"~/.local/share/zeroclaw/whatsapp-session\"","handlingStrategy":"validation","validationCode":"// Before a whatsapp web send, assert web-mode intent on the default alias:\nlet wa = config.channels.whatsapp.get(\"default\").context(\"whatsapp not configured\")?;\nif !wa.is_web_config() {\n    anyhow::bail!(\"configure session_path / pair_phone / mode=personal for whatsapp web send\");\n}","typeGuard":"fn is_whatsapp_web_ready(cfg: &zeroclaw_config::schema::WhatsAppChannelConfig) -> bool {\n    cfg.is_web_config() // session_path | pair_phone | pair_code | ws_url | mode == Personal\n}","tryCatchPattern":"Err(err) if err.to_string().contains(\"requires Web mode\") => {\n    // guide operator: set channels.whatsapp.default.mode = \"personal\" (or session_path), then retry\n}","preventionTips":["Set `mode = \"personal\"` or a session_path the moment you add a whatsapp web section","Do not mix Cloud API fields with this web send path; route cloud sends to the cloud webhook path","Validate is_web_config() in a pre-send check rather than relying on the runtime bail"],"tags":["whatsapp","channel-mode","configuration","zeroclaw"],"backgroundTag":"invalid-channel-config-mode","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}