openclaw - 💡(How to fix) Fix [Bug]: delivery-recovery silently re-sends WhatsApp messages whose dispatch returned UNAVAILABLE — duplicate group posts after gateway restart

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…

After the WhatsApp Web listener disconnects, openclaw message send returns errorCode=UNAVAILABLE ("No active WhatsApp Web listener") to the caller, but the same delivery is also enqueued for delivery-recovery and replayed automatically on the next gateway start. The caller has already treated the send as failed (and may have retried manually), so the recovery replay produces duplicate posts in the destination chat. Reproduced today on a live broadcast group with 3 visible duplicates of the same announcement.

Bug type: Behavior bug (incorrect output/state without crash) Beta release blocker: No

Error Message

2026-05-08T12:43:37.488 [health-monitor] [whatsapp:default] health-monitor: restarting (reason: disconnected)

2026-05-08T13:20:32.132 [ws] ⇄ res ✗ send 93ms errorCode=UNAVAILABLE errorMessage=Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default. channel=whatsapp conn=<conn><conn> id=<id><id>

2026-05-08T13:20:41.857 [ws] ⇄ res ✗ send 91ms errorCode=UNAVAILABLE errorMessage=Error: No active WhatsApp Web listener (account: default). … channel=whatsapp conn=<conn><conn> id=<id><id>

2026-05-08T13:21:07.463 [gateway] http server listening (6 plugins: bluebubbles, browser, memory-core, slack, telegram, whatsapp; 3.8s) 2026-05-08T13:21:09.279 [whatsapp] [default] starting provider (<phone redacted>) 2026-05-08T13:21:09.616 [delivery-recovery] Found 9 pending delivery entries — starting recovery 2026-05-08T13:21:22.412 [delivery-recovery] Recovered delivery 7fb60c46-58f6-42f0-ac84-9756a986caf1 on whatsapp 2026-05-08T13:21:24.652 [delivery-recovery] Recovered delivery 2c4c60f0-8052-4de3-8753-dd0ac4aeb633 on whatsapp 2026-05-08T13:21:24.652 [delivery-recovery] Delivery recovery complete: 2 recovered, 7 failed, 0 skipped (max retries), 0 deferred (backoff)

2026-05-08T13:22:01.006 [ws] ⇄ res ✓ send 1741ms channel=whatsapp conn=<conn><conn> id=<id><id> [whatsapp] Sent message <wa-id-redacted> -> sha256:687f9c6e0d05 (media) (1615ms)

Root Cause

Additional information

  • The error message returned in the UNAVAILABLE response actively suggests the send did not happen ("Start the gateway, then link WhatsApp with: …"). If the design intent is that these deliveries are deferred and replayed, the response should say so (e.g. errorCode=DEFERRED with the recovery uuid), so callers do not double-send.
  • Possible remediations to consider, in order of preference:
    1. Do not enqueue delivery-recovery entries when the dispatch failed with UNAVAILABLE because the listener was down — return a terminal failure.
    2. If recovery is desired, change the WS response so the caller can distinguish "queued for retry" from "failed".
    3. Expose the pending recovery queue to callers (e.g. openclaw deliveries list/cancel) so an agent can clear deferred deliveries before retrying.
  • Workaround in use locally: pre-dispatch idempotency lock at the wrapper layer; this only protects callers that go through the wrapper. The underlying behavior still bites any direct openclaw message send user.
  • Related: this is the WhatsApp side of a broader pattern previously discussed in #75122 (event-loop blocking causing 408 disconnects). The disconnect path is upstream; this report is specifically about what delivery-recovery does after such a disconnect.

Fix Action

Fix / Workaround

Steps to reproduce

  1. Start OpenClaw 2026.5.7 with WhatsApp account logged in and a working Web session.
  2. Force the WhatsApp Web listener into the disconnected state (e.g. let health-monitor mark it disconnected, or close the linked Web session).
  3. While the listener is down, call:
    openclaw message send --channel whatsapp --target <GROUP_JID> --message "<unique text>"
    Observe the CLI/WS response: errorCode=UNAVAILABLE, errorMessage="No active WhatsApp Web listener (account: default)…". Repeat once more so two attempts are recorded.
  4. Restart the gateway (or wait for the listener to come back).
  5. After the listener reports Listening for personal WhatsApp inbound messages, observe [delivery-recovery] Recovered delivery <uuid> on whatsapp for each previously-rejected attempt.
  6. Inspect the destination WhatsApp group. The "recovered" deliveries are visible there even though their dispatch had returned UNAVAILABLE to the caller.

Additional information

  • The error message returned in the UNAVAILABLE response actively suggests the send did not happen ("Start the gateway, then link WhatsApp with: …"). If the design intent is that these deliveries are deferred and replayed, the response should say so (e.g. errorCode=DEFERRED with the recovery uuid), so callers do not double-send.
  • Possible remediations to consider, in order of preference:
    1. Do not enqueue delivery-recovery entries when the dispatch failed with UNAVAILABLE because the listener was down — return a terminal failure.
    2. If recovery is desired, change the WS response so the caller can distinguish "queued for retry" from "failed".
    3. Expose the pending recovery queue to callers (e.g. openclaw deliveries list/cancel) so an agent can clear deferred deliveries before retrying.
  • Workaround in use locally: pre-dispatch idempotency lock at the wrapper layer; this only protects callers that go through the wrapper. The underlying behavior still bites any direct openclaw message send user.
  • Related: this is the WhatsApp side of a broader pattern previously discussed in #75122 (event-loop blocking causing 408 disconnects). The disconnect path is upstream; this report is specifically about what delivery-recovery does after such a disconnect.

Code Example

openclaw message send --channel whatsapp --target <GROUP_JID> --message "<unique text>"

---

2026-05-08T12:43:37.488 [health-monitor] [whatsapp:default] health-monitor: restarting (reason: disconnected)

2026-05-08T13:20:32.132 [ws] ⇄ res ✗ send 93ms errorCode=UNAVAILABLE
    errorMessage=Error: No active WhatsApp Web listener (account: default).
    Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.
    channel=whatsapp conn=<conn><conn> id=<id><id>

2026-05-08T13:20:41.857 [ws] ⇄ res ✗ send 91ms errorCode=UNAVAILABLE
    errorMessage=Error: No active WhatsApp Web listener (account: default). 
    channel=whatsapp conn=<conn><conn> id=<id><id>

2026-05-08T13:21:07.463 [gateway] http server listening (6 plugins: bluebubbles, browser, memory-core, slack, telegram, whatsapp; 3.8s)
2026-05-08T13:21:09.279 [whatsapp] [default] starting provider (<phone redacted>)
2026-05-08T13:21:09.616 [delivery-recovery] Found 9 pending delivery entries — starting recovery
2026-05-08T13:21:22.412 [delivery-recovery] Recovered delivery 7fb60c46-58f6-42f0-ac84-9756a986caf1 on whatsapp
2026-05-08T13:21:24.652 [delivery-recovery] Recovered delivery 2c4c60f0-8052-4de3-8753-dd0ac4aeb633 on whatsapp
2026-05-08T13:21:24.652 [delivery-recovery] Delivery recovery complete: 2 recovered, 7 failed, 0 skipped (max retries), 0 deferred (backoff)

2026-05-08T13:22:01.006 [ws] ⇄ res ✓ send 1741ms channel=whatsapp conn=<conn><conn> id=<id><id>
[whatsapp] Sent message <wa-id-redacted> -> sha256:687f9c6e0d05 (media) (1615ms)
RAW_BUFFERClick to expand / collapse

Summary

After the WhatsApp Web listener disconnects, openclaw message send returns errorCode=UNAVAILABLE ("No active WhatsApp Web listener") to the caller, but the same delivery is also enqueued for delivery-recovery and replayed automatically on the next gateway start. The caller has already treated the send as failed (and may have retried manually), so the recovery replay produces duplicate posts in the destination chat. Reproduced today on a live broadcast group with 3 visible duplicates of the same announcement.

Bug type: Behavior bug (incorrect output/state without crash) Beta release blocker: No

Steps to reproduce

  1. Start OpenClaw 2026.5.7 with WhatsApp account logged in and a working Web session.
  2. Force the WhatsApp Web listener into the disconnected state (e.g. let health-monitor mark it disconnected, or close the linked Web session).
  3. While the listener is down, call:
    openclaw message send --channel whatsapp --target <GROUP_JID> --message "<unique text>"
    Observe the CLI/WS response: errorCode=UNAVAILABLE, errorMessage="No active WhatsApp Web listener (account: default)…". Repeat once more so two attempts are recorded.
  4. Restart the gateway (or wait for the listener to come back).
  5. After the listener reports Listening for personal WhatsApp inbound messages, observe [delivery-recovery] Recovered delivery <uuid> on whatsapp for each previously-rejected attempt.
  6. Inspect the destination WhatsApp group. The "recovered" deliveries are visible there even though their dispatch had returned UNAVAILABLE to the caller.

Expected behavior

Either:

  • a UNAVAILABLE failure returned to the caller is final and the delivery is not re-queued for delivery-recovery; or
  • the delivery is queued but the caller is told (different errorCode / status field) that the send was deferred, so the agent does not retry manually.

The caller's view of the send and the gateway's view must agree. A send that the caller has been told failed must not silently succeed later.

Actual behavior

  • WS response to the caller carries errorCode=UNAVAILABLE and an actionable error message instructing to start the gateway and re-link WhatsApp — which strongly implies "your send did not happen".
  • The gateway nonetheless enqueues the delivery; on the next listener start, delivery-recovery reports Recovered delivery <uuid> on whatsapp, and the message is posted to the live group.
  • Combined with a manual retry by the caller after the gateway restart, the same announcement was posted 3× to the same group.

OpenClaw version

2026.5.7 (eeef486)

Operating system

macOS 26.4.1 (arm64)

Install method

npm global

Model

anthropic/claude-opus-4-7 (not relevant to the failure path)

Provider / routing chain

openclaw → anthropic (direct)

Additional provider/model setup details

Not relevant. The failure path is between openclaw message send (CLI), the gateway WhatsApp plugin, and delivery-recovery.

Logs, screenshots, and evidence

Sanitized excerpt from ~/.openclaw/logs/gateway.log (timezone GMT+3). UUIDs preserved; group JID, phone, conn/message ids and the WhatsApp message id are redacted.

2026-05-08T12:43:37.488 [health-monitor] [whatsapp:default] health-monitor: restarting (reason: disconnected)

2026-05-08T13:20:32.132 [ws] ⇄ res ✗ send 93ms errorCode=UNAVAILABLE
    errorMessage=Error: No active WhatsApp Web listener (account: default).
    Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.
    channel=whatsapp conn=<conn>…<conn> id=<id>…<id>

2026-05-08T13:20:41.857 [ws] ⇄ res ✗ send 91ms errorCode=UNAVAILABLE
    errorMessage=Error: No active WhatsApp Web listener (account: default). …
    channel=whatsapp conn=<conn>…<conn> id=<id>…<id>

2026-05-08T13:21:07.463 [gateway] http server listening (6 plugins: bluebubbles, browser, memory-core, slack, telegram, whatsapp; 3.8s)
2026-05-08T13:21:09.279 [whatsapp] [default] starting provider (<phone redacted>)
2026-05-08T13:21:09.616 [delivery-recovery] Found 9 pending delivery entries — starting recovery
2026-05-08T13:21:22.412 [delivery-recovery] Recovered delivery 7fb60c46-58f6-42f0-ac84-9756a986caf1 on whatsapp
2026-05-08T13:21:24.652 [delivery-recovery] Recovered delivery 2c4c60f0-8052-4de3-8753-dd0ac4aeb633 on whatsapp
2026-05-08T13:21:24.652 [delivery-recovery] Delivery recovery complete: 2 recovered, 7 failed, 0 skipped (max retries), 0 deferred (backoff)

2026-05-08T13:22:01.006 [ws] ⇄ res ✓ send 1741ms channel=whatsapp conn=<conn>…<conn> id=<id>…<id>
[whatsapp] Sent message <wa-id-redacted> -> sha256:687f9c6e0d05 (media) (1615ms)

Sequence:

  1. 12:43 — listener disconnected.
  2. 13:20:32 / 13:20:41 — two send calls returned UNAVAILABLE to the caller. The caller marked these as failed.
  3. 13:21:07 — gateway restart.
  4. 13:21:22 / 13:21:24 — delivery-recovery recovered the same two deliveries it had just told the caller were unavailable; the messages were posted to the destination group.
  5. 13:22:01 — the caller's retry (after restart) succeeded, producing a third post of the same announcement.

Net effect on the destination group: 3 duplicates of the same broadcast.

Impact and severity

  • Affected: any caller of openclaw message send on channel=whatsapp (CLI, agent, plugin) that handles errorCode=UNAVAILABLE as "failed" and retries. Channel-agnostic in principle wherever delivery-recovery is wired up.
  • Severity: High for users sending to public/group destinations. Duplicate posts to a broadcast group are user-visible and difficult to undo (manual deletion only). Also undermines caller-side dedup logic, since the dedup check happens before the recovered replay.
  • Frequency: Reproduced today; this fire-and-forget pattern (UNAVAILABLE returned to caller, message later delivered) maps to at least 5 duplicate-send incidents observed locally over the past 8 days.
  • Consequence: Duplicate broadcasts to live groups (one observed today: 3× the same announcement). Trust impact on automated outbound posting.

Additional information

  • The error message returned in the UNAVAILABLE response actively suggests the send did not happen ("Start the gateway, then link WhatsApp with: …"). If the design intent is that these deliveries are deferred and replayed, the response should say so (e.g. errorCode=DEFERRED with the recovery uuid), so callers do not double-send.
  • Possible remediations to consider, in order of preference:
    1. Do not enqueue delivery-recovery entries when the dispatch failed with UNAVAILABLE because the listener was down — return a terminal failure.
    2. If recovery is desired, change the WS response so the caller can distinguish "queued for retry" from "failed".
    3. Expose the pending recovery queue to callers (e.g. openclaw deliveries list/cancel) so an agent can clear deferred deliveries before retrying.
  • Workaround in use locally: pre-dispatch idempotency lock at the wrapper layer; this only protects callers that go through the wrapper. The underlying behavior still bites any direct openclaw message send user.
  • Related: this is the WhatsApp side of a broader pattern previously discussed in #75122 (event-loop blocking causing 408 disconnects). The disconnect path is upstream; this report is specifically about what delivery-recovery does after such a disconnect.

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

Either:

  • a UNAVAILABLE failure returned to the caller is final and the delivery is not re-queued for delivery-recovery; or
  • the delivery is queued but the caller is told (different errorCode / status field) that the send was deferred, so the agent does not retry manually.

The caller's view of the send and the gateway's view must agree. A send that the caller has been told failed must not silently succeed later.

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Bug]: delivery-recovery silently re-sends WhatsApp messages whose dispatch returned UNAVAILABLE — duplicate group posts after gateway restart