WhatsApp watchdog idle counter doesn't reset on reconnect, causing 60s flap loops
Observed behavior
When no WhatsApp messages are received for 30+ minutes, the watchdog timer (MESSAGE_TIMEOUT_MS, default 1800s) triggers a reconnect. However, active.lastInboundAt carries over across reconnects — it tracks time since last actual message, not time since last connection. This means:
- No messages for 30 min → watchdog fires, closes connection (status 499)
- Reconnect succeeds in ~2-3s
- Watchdog checks again 60s later (WATCHDOG_CHECK_MS)
lastInboundAt still stale → fires again immediately
- Loop repeats every ~60s indefinitely until a real message arrives or gateway restarts
Impact
- Gateway flaps every 60 seconds, generating system event noise
- Each heartbeat cycle burns context/tokens processing disconnect/reconnect events
- Only stops when a real inbound message arrives (resets the counter) or full gateway restart
Root cause
In channel.runtime-Cu2TxQlg.js:2531-2651:
MESSAGE_TIMEOUT_MS = tuning.messageTimeoutMs ?? 1800 * 1e3 (30 min)
WATCHDOG_CHECK_MS = tuning.watchdogCheckMs ?? 60 * 1e3 (1 min)
- On reconnect,
createActiveConnectionRun(status.lastInboundAt ?? status.lastMessageAt ?? null) inherits the old timestamp
- The watchdog interval fires, sees stale timestamp, triggers another reconnect
Expected behavior
After a watchdog-triggered reconnect, either:
- Reset
lastInboundAt to Date.now() on successful reconnect, OR
- Add backoff to watchdog reconnects (double the check interval after each watchdog-triggered close), OR
- Track that the last close was watchdog-initiated and skip one cycle
Workarounds
- Gateway restart resets everything (but only lasts until next 30-min idle period)
- Having actual messages arrive resets the counter naturally
Environment
- OpenClaw 2026.3.23-1
- WhatsApp Web channel, single account
- Observed 4+ flap cycles in one day (2026-03-24), each requiring manual gateway restart
WhatsApp watchdog idle counter doesn't reset on reconnect, causing 60s flap loops
Observed behavior
When no WhatsApp messages are received for 30+ minutes, the watchdog timer (MESSAGE_TIMEOUT_MS, default 1800s) triggers a reconnect. However,
active.lastInboundAtcarries over across reconnects — it tracks time since last actual message, not time since last connection. This means:lastInboundAtstill stale → fires again immediatelyImpact
Root cause
In
channel.runtime-Cu2TxQlg.js:2531-2651:MESSAGE_TIMEOUT_MS = tuning.messageTimeoutMs ?? 1800 * 1e3(30 min)WATCHDOG_CHECK_MS = tuning.watchdogCheckMs ?? 60 * 1e3(1 min)createActiveConnectionRun(status.lastInboundAt ?? status.lastMessageAt ?? null)inherits the old timestampExpected behavior
After a watchdog-triggered reconnect, either:
lastInboundAttoDate.now()on successful reconnect, ORWorkarounds
Environment