#WhatsApp send fails with "No active WhatsApp Web listener"

1 messages ¡ Page 1 of 1 (latest)

kindred isle
#

Bug: WhatsApp ⁠ send ⁠ fails with "No active WhatsApp Web listener" — but inbound + react work fine

Version: OpenClaw 2026.3.13-1 (61d171a) / macOS 13 / Node v24.13.1

Problem:
Both ⁠ openclaw message send ⁠ (CLI) and the agent ⁠ message ⁠ tool with ⁠ action=send ⁠ always fail with:

No active WhatsApp Web listener (account: default)

However, inbound messages work perfectly and ⁠ action=react ⁠ from the agent tool also works. Only outbound ⁠ send ⁠ is broken.

Diagnostics:

⁠ openclaw gateway status ⁠ — Config (cli) and Config (service) both point to ⁠ ~/.openclaw/openclaw.json ⁠ ✅ No mismatch.

⁠ openclaw channels status ⁠:

WhatsApp default: enabled, configured, linked, running, connected, dm:allowlist

⁠ openclaw channels status --probe --json ⁠ (WhatsApp section):
⁠ json
{
"configured": true, "linked": true, "running": true,
"connected": true, "reconnectAttempts": 0,
"lastError": null, "lastOutboundAt": null
}
 ⁠

Note: ⁠ lastOutboundAt: null ⁠ — outbound has never worked.

What I tried:
•⁠ ⁠Restarting gateway multiple times
•⁠ ⁠Re-linking WhatsApp (⁠ channels login ⁠ says "✅ Linked!")
•⁠ ⁠Clearing session data + fresh re-link
•⁠ ⁠Sending with and without ⁠ --account default ⁠
•⁠ ⁠Different target formats (E.164, JID)
•⁠ ⁠⁠ --json --verbose ⁠ — no extra info, same error

Expected: ⁠ action=send ⁠ should work when WhatsApp shows as fully connected with no errors and other actions (receive, react) work fine.

Looks like the send codepath checks a listener map that isn't getting populated even though the WhatsApp monitor is running and connected.

rain groveBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

orchid moatBOT
#

Short answer — what the error means and the two most likely causes

  • The "No active WhatsApp Web listener (account: default)" error comes from requireActiveWebListener(), which throws when the in‑process listeners Map (extensions/whatsapp/src/active-listener.ts) has no entry for that account id.
  • That Map is populated by the WhatsApp monitor (monitorWebChannel → setActiveWebListener) when Baileys connects. So either:
    1. the monitor never registered a listener under the same account id that your send call is using (account-id mismatch), or
    2. the code that handles the send is running in a different process / module instance that does not share the listeners Map (service vs CLI install mismatch or sandbox/worker isolation / duplicate module loads).

What to check (quick triage, in this order)

  1. Tail the gateway logs while reproducing the failure
  • Terminal A (tail logs):
    openclaw logs --follow
  • Terminal B (reproduce):
    openclaw message send --channel whatsapp --to +1555... --message "test" --account default --json --verbose
  • In the gateway logs look for these markers (timestamps should line up with your send attempt):
    • "WhatsApp gateway connected as +<E.164>" ← monitor set the listener / connected
    • any "web-auto-reply" / "web-heartbeat" connect logs
    • "Sending message -> ..." (send.ts outboundLog.info)
    • "Sent message ..." OR the error "No active WhatsApp Web listener (account: ...)" and the stack lines after it
#

If you see "connected" but the send attempt logs show the error, paste the relevant log block (connect + send attempt + error).

  1. Confirm which account id the monitor registered
  • Search logs for the connect line:
    openclaw logs --follow | rg -i "WhatsApp gateway connected" || openclaw logs --follow
  • That line tells you the self E.164 the monitor connected as. Compare that to the account id you pass to send (try both with --account default and --account <the-account-id-from-config>).
  1. Verify channel / account runtime snapshot
  • Run:
    openclaw channels status --probe --verbose --json
  • Paste the WhatsApp account section. We want to see configured/linked/connected and any runtime/audit fields.
  1. Check for multiple openclaw installs / gateway process mismatch
  • On the gateway host run:
    which openclaw
    openclaw --version
    openclaw gateway status
#

ps aux | rg openclaw
Look at the "entrypoint" / service path in gateway status and make sure the CLI's openclaw (which openclaw) is the same install as the daemon. Mismatched installs (CLI vs service) is a common source of "the gateway thinks it's connected but the process actually handling the RPCs doesn't have the listener state".

  1. Check config → account ids
  • Print WhatsApp config (to confirm what account ids exist and what default is):
    openclaw config get channels.whatsapp
    Look at accounts keys and defaultAccount. Try sending with the explicit account id you see there:
    openclaw message send --channel whatsapp --to +1555... --message "test" --account <that-account-id>
  1. If you want a quick focused grep (optional):
    openclaw logs --follow | rg --line-buffered -i "WhatsApp gateway connected|No active WhatsApp Web listener|web-outbound|Sending message|Sent message|sendReaction"

What these results mean (how to interpret what you find)

  • You see "WhatsApp gateway connected as +...": monitor is running and setActiveWebListener ran. If you still get No active listener on send:
    • Check the account id in that "connected as" line and the account used by your send. If they differ, that's the cause (send uses 'default' but listener was registered under another id).
    • If the account ids match, but requireActiveWebListener still fails, that suggests the gateway process handling the RPC does not share the same module instance as the monitor (duplicate installs or the RPC landed on a different binary). Paste gateway status + ps output and I can confirm.
#
  • You do NOT see any "WhatsApp gateway connected" logs while inbound messages are nevertheless arriving: that would be surprising, but it could mean inbound handling uses a direct socket reference that doesn't set the shared listener in the same way; paste the inbound log lines and I'll look.

Possible quick workarounds / fixes to try now

  • Explicitly pass the account id you see in the config / connect line (not just "default"), e.g. --account work or --account "+1555xxxx" to match the monitor.
  • If the gateway binary and CLI appear to be from different installs, reinstall/repair the gateway service so both use the same installation:
    • (non-destructive) openclaw gateway install --force
    • then openclaw gateway restart
    • (or run openclaw doctor and paste its output)

What I need from you if the above doesn't reveal/solve it

  • paste the relevant slices of the gateway log around a reproduce attempt (connect + send attempt + error). Example: 30s before and 10s after your send command.
  • outputs of:
    • openclaw gateway status
    • openclaw channels status --probe --verbose --json
    • which openclaw
    • openclaw --version
    • ps aux | rg openclaw
#
  • openclaw config get channels.whatsapp

If you paste those I'll diagnose exactly whether this is an account-id mismatch, duplicate install/process mismatch, or a runtime bug (module duplication / sandboxing). If it is a bug in OpenClaw (listeners being set in a different module instance), I can point to the minimal repro and propose a fix (ensure the active-listener map is shared on the gateway RPC path).

If you want, run the log capture command and paste the block here now and I’ll read it and tell you the exact next step.

kindred isle
#

Ran all diagnostics. Results:

Same install confirmed:

which openclaw → /Users/maxclaw/.nvm/versions/node/v24.13.1/bin/openclaw
Version: 2026.3.13 (61d171a)
Single process: pid 95692
Gateway config (cli) = config (service) = ~/.openclaw/openclaw.json

Account config:
Only one WhatsApp account: ⁠ default ⁠. No ⁠ defaultAccount ⁠ override set.

Log capture around send attempt (ran ⁠ ```openclaw message send --channel whatsapp --target "+34655112233" --message "test" ⁠ --message "test" --account default ⁠:



0:03:46 [ws] ⇄ res ✓ chat.send 145ms
00:03:46 [tools] message failed: Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.

No "WhatsApp gateway connected as +..." marker anywhere in logs. No stack trace beyond the error message.

Meanwhile ⁠ channels status ⁠ shows:

WhatsApp default: enabled, configured, linked, running, connected
reconnectAttempts: 0, lastError: null, lastOutboundAt: null


Inbound messages work. ⁠ action=react ⁠ from agent tool works. Only ⁠ action=send ⁠ (CLI and agent tool) fails. ⁠ lastOutboundAt: null ⁠ — outbound has never succeeded on this install.

Single gateway process (pid 95692), single install path, single config file, single WhatsApp account ⁠ default ⁠. No duplicates.

Does this match the case where inbound uses a direct socket reference that doesn't call ⁠ setActiveWebListener ⁠?
orchid moatBOT
#

Yep — what you’re seeing is consistent with “inbound is flowing via the Baileys socket, but the global ActiveWebListener registry was never populated”.

A key nuance in OpenClaw’s WhatsApp implementation:

  • Inbound + auto-reply can work without the ActiveWebListener map, because replies in the inbound pipeline can go through the per-message msg.reply(...) path.
  • But message send (CLI + tool action=send) goes through sendMessageWhatsApp(...), which starts with requireActiveWebListener(...) (the Map in extensions/whatsapp/src/active-listener.ts). If that Map is empty → you get exactly your error.

What should happen (and why this looks like a real bug)

  • The WhatsApp channel gateway start path is gateway.startAccount → monitorWebChannel(...).
  • monitorWebChannel(...) is supposed to call setActiveWebListener(accountId, listener) after connect.
  • So in a healthy run, if channels status says running+connected, the listener Map should be set and message send should work.

Given you have:

  • single PID
  • single install path
  • single WhatsApp account (default)
  • and still never had a successful outbound send (lastOutboundAt: null)
#

…this points to either:

  1. the monitor never reaches/executes setActiveWebListener (crash/early-return on startup path), or
  2. there are two loaded copies of the active-listener module in the same process (so the monitor sets listener in Map A, but send checks Map B). This can happen even with one PID if the plugin/runtime ends up importing WhatsApp code from two different physical paths.

Two high-signal checks that usually reveal which one it is

1) Check the service logs (macOS) — not just openclaw logs

On macOS the daemon often writes extra detail to:

tail -n 200 ~/.openclaw/logs/gateway.log
tail -n 200 ~/.openclaw/logs/gateway.err.log

Look for:

  • [default] starting provider (...)
  • any exceptions around WhatsApp startup / reconnect
  • anything mentioning active-listener, monitorWebChannel, web-inbound, baileys
#

If there’s an exception happening after “connected” status is set but before setActiveWebListener, it’ll usually show up here (and won’t always show in the structured /tmp/openclaw/*.log stream).

2) Confirm whether “react works” is the same codepath

In current code, WhatsApp “react” action ultimately calls sendReactionWhatsApp(...), which also uses requireActiveWebListener(...).

So if the listener Map is truly empty, react should fail with the same error unless you’re seeing an ackReaction or some other path.

If you can paste one log line from a successful react (the tool invocation + the result), that will clarify a lot.

One small config sanity check
Because WhatsApp enablement is gated by the top-level web section:

openclaw config get web

Make sure you don’t have web: { enabled: false } (it would disable the provider start). Your status suggests it’s running, but it’s worth confirming.

#

Next step (this is now issue-worthy)
With the evidence you already gathered, this is basically ready for a GitHub bug report. If you file one, include:

  • your exact version + macOS + node
  • the channels status --probe --json WhatsApp block
  • the send failure log line (you already have it)
  • last ~200 lines of ~/.openclaw/logs/gateway.err.log and gateway.log

If you paste the gateway.err.log tail here, I can tell you immediately whether this is “startup exception prevents listener registration” vs “duplicate module instance / wrong import path” (and what workaround is most likely to get you unstuck in the meantime).

kindred isle
#

Two important findings:

  1. ⁠ react ⁠ WORKS — and uses the same ⁠ requireActiveWebListener ⁠:
    Just confirmed: ⁠ action=react ⁠ succeeded (emoji 👀, result ⁠ {"ok": true} ⁠). If ⁠ sendReactionWhatsApp ⁠ also calls ⁠ requireActiveWebListener ⁠, then the listener Map IS populated. The issue is specific to the ⁠ sendMessageWhatsApp ⁠ codepath.

  2. Gateway logs (⁠ ~/.openclaw/logs/ ⁠):
    No ⁠ gateway.log ⁠ or ⁠ gateway.err.log ⁠ files exist in ⁠ ~/.openclaw/logs/ ⁠. Logs only go to ⁠ /tmp/openclaw/openclaw-*.log ⁠. Relevant lines around the react success + send failure are identical to what I shared before — the send just throws "No active WhatsApp Web listener" with no additional stack.

  3. ⁠ openclaw config get web ⁠: returns empty/undefined — no explicit ⁠ web ⁠ config set.

So the listener Map has the entry (proven by react), but ⁠ sendMessageWhatsApp ⁠ can't find it. This points to either:
•⁠ ⁠A different import path for ⁠ requireActiveWebListener ⁠ in ⁠ send.ts ⁠ vs ⁠ reaction.ts ⁠ (duplicate module within same process)
•⁠ ⁠Or an additional check in the send path that fails before reaching the listener

Should I open a GitHub issue with all the evidence?

orchid moatBOT
#

Yes — at this point you’ve got enough signal to open a GitHub issue, and I think you should.

Because (as you noticed) if react succeeds but send fails with “No active WhatsApp Web listener”, the only explanations that really fit are:

  1. Duplicate module instance in the same process (most likely)
    sendMessageWhatsApp() and the reaction path are effectively consulting different listeners Maps because active-listener got loaded twice under different physical paths/URLs.
    (Even with one PID + one “openclaw” binary, this can happen if there are two copies of the WhatsApp extension code in the module graph.)

  2. AccountId mismatch is basically ruled out (you only have default, and the error names default).

It’s not consistent with “an extra check in send fails before the listener lookup” because the thrown error is literally from requireActiveWebListener(...).

What to include in the issue (so it’s actionable)

Along with what you already pasted, add these two bits:

A) Prove there are (or aren’t) two physical copies of the listener module on disk
Run (on the gateway host):

#
OPENCLAW_DIR="$(cd "$(dirname "$(which openclaw)")/../lib/node_modules/openclaw" && pwd)"
echo "$OPENCLAW_DIR"
find "$OPENCLAW_DIR" -name 'active-listener.*' -o -name 'send.*' -o -name 'auto-reply.*' | rg 'whatsapp/src/(active-listener|send|auto-reply)'

If you see both a dist/.../active-listener.js and another .../active-listener.js that could plausibly be imported, that’s your smoking gun.

B) A minimal “react works / send fails” repro

  • The exact tool call / CLI command you used for react (and its ok result)
  • The exact tool call / CLI command you used for send (and the error)
  • The WhatsApp part of openclaw channels status --probe --json

Where to file

Open here: https://github.com/openclaw/openclaw/issues

Title suggestion:

#

WhatsApp: message send fails “No active WhatsApp Web listener” but react succeeds (likely duplicate active-listener module)

If you paste the GitHub issue link back into this thread after you file it, I can help tighten the report / add any extra diagnostic command that would make it easiest to fix.

kindred isle
orchid moatBOT
#

Nice — that issue writeup is exactly the kind of “maintainer-friendly” report that gets fixed quickly.

Two small tweaks that will make it even more actionable:

  1. In the issue body you currently have:

find "$OPENCLAW_DIR" -name 'active-listener.*' | grep whatsapp → [results]

If you can, reply on the issue with the actual output (full paths). The whole “duplicate module instance” hypothesis lives or dies on seeing two different physical paths to WhatsApp’s active-listener / send modules.

  1. Also add a quick check for nested node_modules/openclaw copies under the global install dir (this is the most common way you end up with “same code, two Maps” in one PID):
OPENCLAW_DIR="$(cd "$(dirname "$(which openclaw)")/../lib/node_modules/openclaw" && pwd)"
echo "OPENCLAW_DIR=$OPENCLAW_DIR"