{"record":{"id":"99126615445c7495","repo":"zeroclaw-labs/zeroclaw","slug":"exceeded-reconnect-attempts-giving-up","errorCode":null,"errorMessage":"exceeded {} reconnect attempts, giving up","messagePattern":"exceeded (.+?) reconnect attempts, giving up","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/zeroclaw-channels/src/whatsapp_web.rs","lineNumber":2995,"sourceCode":"            let handle = self.bot_handle.lock().take();\n            if let Some(handle) = handle {\n                handle.abort();\n                // Await the aborted task so background I/O finishes before\n                // we delete session files.\n                let _ = handle.await;\n            }\n\n            // Drop bot/device so the SQLite connection is closed\n            // before we remove session files (releases WAL/SHM locks).\n            // `backend` was moved into the builder, so dropping `bot`\n            // releases the last Arc reference to the storage backend.\n            drop(bot);\n            drop(device);\n\n            if should_reconnect {\n                let (attempts, exceeded) = Self::record_retry(&retry_count);\n                if exceeded {\n                    anyhow::bail!(\n                        \"exceeded {} reconnect attempts, giving up\",\n                        Self::MAX_RETRIES\n                    );\n                }\n\n                // Only purge session files when LoggedOut was explicitly observed.\n                // A transient task crash (Err from recv) should not wipe a valid session.\n                if Self::should_purge_session(&session_revoked) {\n                    for path in Self::session_file_paths(&expanded_session_path) {\n                        match tokio::fs::remove_file(&path).await {\n                            Ok(()) => {}\n                            Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}\n                            Err(e) => ::zeroclaw_log::record!(\n                                WARN,\n                                ::zeroclaw_log::Event::new(\n                                    module_path!(),\n                                    ::zeroclaw_log::Action::Note\n                                )","sourceCodeStart":2977,"sourceCodeEnd":3013,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-channels/src/whatsapp_web.rs#L2977-L3013","documentation":"`listen()` runs a reconnect state machine: on each disconnect it retries with exponential backoff (base 3s, doubling, capped at 300s) for up to `MAX_RETRIES` (10) attempts. When the counter reports `exceeded`, the loop gives up and `listen()` returns this error — the channel stays down until something restarts it. Session files are purged only when an explicit LoggedOut was observed, so reaching this error usually means repeated transient failures (network, protocol) or repeated forced logouts.","triggerScenarios":"Ten consecutive reconnect failures: a prolonged network outage, WhatsApp protocol/backend incompatibility, the phone number being re-linked elsewhere (forcing logout every reconnect), or the bot task crashing on startup repeatedly.","commonSituations":"Server losing network overnight; another device linking the same number and invalidating the session each time; version skew with the WhatsApp Web protocol after an update.","solutions":["Check the zeroclaw logs immediately before the give-up to identify the disconnect cause (LoggedOut vs task crash vs network).","Restore connectivity and restart the process or `listen()` — the attempt counter resets on restart.","If LoggedOut repeats, delete the session files and re-pair by scanning the QR.","Keep zeroclaw updated so the vendored WhatsApp Web backend tracks protocol changes."],"exampleFix":"// before: run listen() once; the process gives up after 10 failed reconnects\nif let Err(e) = channel.listen(tx).await {\n    tracing::error!(\"channel died: {e}\");\n}\n\n// after: supervise with restart so the retry counter resets\nloop {\n    if let Err(e) = channel.listen(tx.clone()).await {\n        tracing::error!(\"whatsapp-web listen failed: {e}; restarting in 60s\");\n        tokio::time::sleep(std::time::Duration::from_secs(60)).await;\n    }\n}","handlingStrategy":"retry","validationCode":"// Check link health before a long-running shift; restart listen() if it already gave up\nif !channel.health_check().await {\n    // the listen task may have exhausted its 10 reconnect attempts\n    restart_channel_task(&channel, tx).await;\n}","typeGuard":null,"tryCatchPattern":"loop {\n    if let Err(e) = channel.listen(tx.clone()).await {\n        if e.to_string().contains(\"reconnect attempts, giving up\") {\n            tracing::error!(\"whatsapp-web gave up reconnecting: {e}; restarting in 60s\");\n            tokio::time::sleep(std::time::Duration::from_secs(60)).await;\n            continue; // fresh listen() resets the attempt counter\n        }\n        return Err(e);\n    }\n}","preventionTips":["Run `listen()` under a supervisor that restarts it with a delay; the retry counter resets each start.","Monitor for repeated LoggedOut events — another device linking the same number will loop you into this error.","Keep the binary current so protocol changes do not masquerade as permanent network failure."],"tags":["whatsapp-web","reconnect","retry","network","rust"],"backgroundTag":"retries-exhausted","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}