openclaw - 💡(How to fix) Fix WhatsApp watchdog idle counter doesn't reset on reconnect, causing 60s flap loops [2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#54332Fetched 2026-04-08 01:28:53
View on GitHub
Comments
2
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
commented ×2

Error Message

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:

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

Fix Action

Fix / Workaround

Workarounds

  • Gateway restart resets everything (but only lasts until next 30-min idle period)
  • Having actual messages arrive resets the counter naturally
RAW_BUFFERClick to expand / collapse

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:

  1. No messages for 30 min → watchdog fires, closes connection (status 499)
  2. Reconnect succeeds in ~2-3s
  3. Watchdog checks again 60s later (WATCHDOG_CHECK_MS)
  4. lastInboundAt still stale → fires again immediately
  5. 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:

  1. Reset lastInboundAt to Date.now() on successful reconnect, OR
  2. Add backoff to watchdog reconnects (double the check interval after each watchdog-triggered close), OR
  3. 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

extent analysis

Fix Plan

To resolve the WhatsApp watchdog idle counter issue, we will implement a reset of lastInboundAt to Date.now() on successful reconnect. Here are the steps:

  • Modify the createActiveConnectionRun function to reset lastInboundAt on reconnect:
createActiveConnectionRun(status.lastInboundAt === null || status.lastInboundAt < Date.now() - MESSAGE_TIMEOUT_MS ? Date.now() : status.lastInboundAt ?? status.lastMessageAt ?? null)

Alternatively, you can add a separate function to handle the reconnect logic:

function resetLastInboundAtOnReconnect(status) {
  if (status.lastInboundAt === null || status.lastInboundAt < Date.now() - MESSAGE_TIMEOUT_MS) {
    return Date.now();
  }
  return status.lastInboundAt ?? status.lastMessageAt ?? null;
}

createActiveConnectionRun(resetLastInboundAtOnReconnect(status))
  • Update the channel.runtime-Cu2TxQlg.js file with the modified code.

Verification

To verify the fix, monitor the gateway for 30 minutes without receiving any WhatsApp messages. The watchdog should trigger a reconnect, and the lastInboundAt timestamp should be reset to the current time. The gateway should not flap every 60 seconds, and the system event noise should be reduced.

Extra Tips

  • Consider adding logging to track the lastInboundAt timestamp and the watchdog reconnects to monitor the fix.
  • Review the MESSAGE_TIMEOUT_MS and WATCHDOG_CHECK_MS values to ensure they are suitable for your production environment.
  • If the issue persists, consider implementing a backoff mechanism for watchdog reconnects or tracking the last close as watchdog-initiated to skip one cycle.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

After a watchdog-triggered reconnect, either:

  1. Reset lastInboundAt to Date.now() on successful reconnect, OR
  2. Add backoff to watchdog reconnects (double the check interval after each watchdog-triggered close), OR
  3. Track that the last close was watchdog-initiated and skip one cycle

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING