zeroclaw-labs/zeroclaw · error · anyhow::Error
WhatsApp channel send requires Web mode (set session_path, p
Error message
WhatsApp channel send requires Web mode (set session_path, pair_phone, or mode = personal)
What it means
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.
Source
Thrown at crates/zeroclaw-channels/src/orchestrator/mod.rs:9126
.with_workspace_dir(workspace_dir)
.with_ack_reactions(ack),
))
}
#[cfg(not(feature = "channel-matrix"))]
{
anyhow::bail!("Matrix channel requires the `channel-matrix` feature");
}
}
"whatsapp" | "whatsapp-web" | "whatsapp_web" => {
#[cfg(feature = "whatsapp-web")]
{
let wa = config
.channels
.whatsapp
.get("default")
.context("WhatsApp channel is not configured")?;
if !wa.is_web_config() {
anyhow::bail!(
"WhatsApp channel send requires Web mode (set session_path, pair_phone, or mode = personal)"
);
}
let alias = "default".to_string();
let peer_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> = {
let cfg_arc = config_arc.clone();
let alias = alias.clone();
Arc::new(move || cfg_arc.read().channel_external_peers("whatsapp", &alias))
};
let allowed_groups_resolver: Arc<dyn Fn() -> Vec<String> + Send + Sync> = {
let cfg_arc = config_arc.clone();
let alias = alias.clone();
Arc::new(move || {
cfg_arc
.read()
.channels
.whatsapp
.get(&alias)View on GitHub (pinned to 88bb9c8533)
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
Example fix
# before (zeroclaw.toml) [channels.whatsapp.default] phone_number_id = "123" access_token = "EAAB..." # error: WhatsApp channel send requires Web mode # after [channels.whatsapp.default] mode = "personal" session_path = "~/.local/share/zeroclaw/whatsapp-session"
Defensive patterns
Strategy: validation
Validate before calling
// Before a whatsapp web send, assert web-mode intent on the default alias:
let wa = config.channels.whatsapp.get("default").context("whatsapp not configured")?;
if !wa.is_web_config() {
anyhow::bail!("configure session_path / pair_phone / mode=personal for whatsapp web send");
} Type guard
fn is_whatsapp_web_ready(cfg: &zeroclaw_config::schema::WhatsAppChannelConfig) -> bool {
cfg.is_web_config() // session_path | pair_phone | pair_code | ws_url | mode == Personal
} Try / catch
Err(err) if err.to_string().contains("requires Web mode") => {
// guide operator: set channels.whatsapp.default.mode = "personal" (or session_path), then retry
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- model_provider `{ref_or_family}` does not resolve to a confi
- agents.{agent_alias}.model_provider is empty; runtime reload
- model_provider `{trimmed}` must use `<type>.<alias>` form
- model_provider `{trimmed}` does not resolve to a configured
- {channel_type} channel alias `{alias}` is not configured. Ru
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/6b6f0b9bb4bcd870.
Report an issue: GitHub.