{"record":{"id":"5d9461252335c38c","repo":"zeroclaw-labs/zeroclaw","slug":"whatsapp-web-client-not-connected-initialize-the","errorCode":null,"errorMessage":"WhatsApp Web client not connected. Initialize the bot first.","messagePattern":"WhatsApp Web client not connected\\. Initialize the bot first\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-channels/src/whatsapp_web.rs","lineNumber":2417,"sourceCode":"            ::zeroclaw_api::attribution::ChannelKind::WhatsappWeb,\n        )\n    }\n    fn alias(&self) -> &str {\n        &self.alias\n    }\n}\n\n#[cfg(feature = \"whatsapp-web\")]\n#[async_trait]\nimpl Channel for WhatsAppWebChannel {\n    fn name(&self) -> &str {\n        \"whatsapp\"\n    }\n\n    async fn send(&self, message: &SendMessage) -> Result<()> {\n        let client = self.client.lock().clone();\n        let Some(client) = client else {\n            anyhow::bail!(\"WhatsApp Web client not connected. Initialize the bot first.\");\n        };\n\n        // Validate recipient allowlist only for direct phone-number targets.\n        if !Self::is_jid(&message.recipient) {\n            let normalized = self.normalize_phone(&message.recipient);\n            if !self.is_number_allowed(&normalized) {\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!(\"recipient {} not in allowed list\", message.recipient)\n                );\n                return Ok(());\n            }\n        }\n\n        let deliverable_recipient = Self::resolve_outbound_recipient(&message.recipient);\n        let to = self.recipient_to_jid(&deliverable_recipient)?;","sourceCodeStart":2399,"sourceCodeEnd":2435,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/whatsapp_web.rs#L2399-L2435","documentation":"The WhatsApp Web channel stores its client behind a lock; it is `Some` only between a successful `listen()` handshake and shutdown. `send` bails with this message when the client is `None` — the bot was never initialized, is still connecting (QR not yet scanned), or has disconnected and is inside the reconnect cycle.","triggerScenarios":"Calling `send` before `listen()` completed pairing; sending while the channel is between a disconnect and its next reconnect attempt; sending on a channel whose `listen()` task has exited or given up.","commonSituations":"Startup races — an outbound greeting or approval request firing before the QR was scanned; sends landing in the reconnect window (backoff up to 300s between attempts); using a channel handle after shutdown.","solutions":["Wait for channel readiness (health/connectivity signal or the first inbound message) before queueing outbound messages.","Ensure `listen()` is running; if it exited, restart the channel (and re-pair if the session was purged).","Retry the send after a delay to ride out the reconnect window."],"exampleFix":"// before: fire immediately at process start\nchannel.send(&greeting).await?; // may race QR pairing\n\n// after: wait for the client to be connected\nwhile !channel.health_check().await {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n}\nchannel.send(&greeting).await?;","handlingStrategy":"retry","validationCode":"// Wait until the WhatsApp Web client is live before sending\nwhile !channel.health_check().await {\n    tokio::time::sleep(std::time::Duration::from_secs(1)).await;\n}\nchannel.send(&msg).await","typeGuard":"async fn web_client_connected(channel: &WhatsAppWebChannel) -> bool {\n    channel.health_check().await\n}","tryCatchPattern":"match channel.send(&msg).await {\n    Err(e) if e.to_string().contains(\"not connected\") => {\n        // reconnect window: wait for the client, then retry once\n        while !channel.health_check().await {\n            tokio::time::sleep(std::time::Duration::from_secs(2)).await;\n        }\n        channel.send(&msg).await\n    }\n    other => other,\n}","preventionTips":["Queue outbound messages until the first inbound event or a positive health check confirms pairing.","Keep `listen()` supervised; a dead listen task makes every send fail this way.","Expect reconnect backoff up to 300s — size send-retry timeouts accordingly."],"tags":["whatsapp-web","connection-state","lifecycle","rust"],"backgroundTag":"client-not-connected","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}