openclaw - 💡(How to fix) Fix WhatsApp outbound sends fail: module-scoped listeners Map duplicated across 27 build chunks [1 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#52781Fetched 2026-04-08 01:19:28
View on GitHub
Comments
1
Participants
2
Timeline
1
Reactions
0
Author
Timeline (top)
commented ×1

Outbound WhatsApp messages via the message tool (proactive sends) fail with:

Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.

Inbound messages and auto-replies work fine. Only proactive/tool-initiated sends are broken.

Error Message

Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.

Root Cause

The src/web/active-listener.ts module contains a module-scoped listeners Map:

const listeners = new Map();

During the Rollup/Vite build, this module gets duplicated into ~27 separate chunks across two bundle directories:

  • dist/*.js (e.g., config-CmS8VEM4.js, auth-profiles-iXW75sRj.js, model-selection-*.js, etc.)
  • dist/plugin-sdk/*.js (e.g., auth-profiles-Cm29uFmx.js, model-auth-*.js, tts-core-*.js, etc.)

Each chunk gets its own copy of const listeners = new Map().

The WhatsApp provider (loaded from plugin-sdk/web-*.js) calls setActiveWebListener() → writes to the listeners Map in its plugin-sdk/ shared module.

The message tool send path (loaded from dist/config-*.js → lazy import("./runtime-whatsapp-outbound.runtime-*.js")) calls requireActiveWebListener() → reads from the listeners Map in its dist/ shared module.

These are two different Maps. The provider registers in Map A, the send tool looks in Map B → empty → error.

Fix Action

Workaround

Replace the module-scoped Map with a globalThis-shared Map in all affected chunks:

- const listeners = /* @__PURE__ */ new Map();
+ const listeners = globalThis.__oc_wa_listeners ??= new Map();

This needs to be applied to all files containing #region src/web/active-listener.ts (27 files in v2026.3.13).

One-liner to patch:

cd /usr/lib/node_modules/openclaw/dist
for f in *.js plugin-sdk/*.js; do
  LINENUM=$(grep -n '#region src/web/active-listener.ts' "$f" 2>/dev/null | head -1 | cut -d: -f1)
  if [ -n "$LINENUM" ]; then
    MAPLINE=$((LINENUM + 1))
    sed -i "${MAPLINE}s|const listeners = /\* @__PURE__ \*/ new Map();|const listeners = globalThis.__oc_wa_listeners \?\?= new Map();|" "$f"
  fi
done

Code Example

Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.

---

const listeners = new Map();

---

- const listeners = /* @__PURE__ */ new Map();
+ const listeners = globalThis.__oc_wa_listeners ??= new Map();

---

cd /usr/lib/node_modules/openclaw/dist
for f in *.js plugin-sdk/*.js; do
  LINENUM=$(grep -n '#region src/web/active-listener.ts' "$f" 2>/dev/null | head -1 | cut -d: -f1)
  if [ -n "$LINENUM" ]; then
    MAPLINE=$((LINENUM + 1))
    sed -i "${MAPLINE}s|const listeners = /\* @__PURE__ \*/ new Map();|const listeners = globalThis.__oc_wa_listeners \?\?= new Map();|" "$f"
  fi
done
RAW_BUFFERClick to expand / collapse

Bug: No active WhatsApp Web listener on proactive/outbound sends (module isolation)

Summary

Outbound WhatsApp messages via the message tool (proactive sends) fail with:

Error: No active WhatsApp Web listener (account: default). Start the gateway, then link WhatsApp with: openclaw channels login --channel whatsapp --account default.

Inbound messages and auto-replies work fine. Only proactive/tool-initiated sends are broken.

Root Cause

The src/web/active-listener.ts module contains a module-scoped listeners Map:

const listeners = new Map();

During the Rollup/Vite build, this module gets duplicated into ~27 separate chunks across two bundle directories:

  • dist/*.js (e.g., config-CmS8VEM4.js, auth-profiles-iXW75sRj.js, model-selection-*.js, etc.)
  • dist/plugin-sdk/*.js (e.g., auth-profiles-Cm29uFmx.js, model-auth-*.js, tts-core-*.js, etc.)

Each chunk gets its own copy of const listeners = new Map().

The WhatsApp provider (loaded from plugin-sdk/web-*.js) calls setActiveWebListener() → writes to the listeners Map in its plugin-sdk/ shared module.

The message tool send path (loaded from dist/config-*.js → lazy import("./runtime-whatsapp-outbound.runtime-*.js")) calls requireActiveWebListener() → reads from the listeners Map in its dist/ shared module.

These are two different Maps. The provider registers in Map A, the send tool looks in Map B → empty → error.

Why Auto-Reply Works

Auto-replies travel through the same plugin-sdk/ bundle as the WhatsApp provider, so they use the same listeners Map instance and find the registered listener.

Reproduction

  1. Start gateway with WhatsApp configured
  2. Send an inbound message → auto-reply works ✅
  3. Use the message tool to proactively send → fails with "No active WhatsApp Web listener" ❌
  4. Use CLI openclaw message send --channel whatsapp → same error ❌

Workaround

Replace the module-scoped Map with a globalThis-shared Map in all affected chunks:

- const listeners = /* @__PURE__ */ new Map();
+ const listeners = globalThis.__oc_wa_listeners ??= new Map();

This needs to be applied to all files containing #region src/web/active-listener.ts (27 files in v2026.3.13).

One-liner to patch:

cd /usr/lib/node_modules/openclaw/dist
for f in *.js plugin-sdk/*.js; do
  LINENUM=$(grep -n '#region src/web/active-listener.ts' "$f" 2>/dev/null | head -1 | cut -d: -f1)
  if [ -n "$LINENUM" ]; then
    MAPLINE=$((LINENUM + 1))
    sed -i "${MAPLINE}s|const listeners = /\* @__PURE__ \*/ new Map();|const listeners = globalThis.__oc_wa_listeners \?\?= new Map();|" "$f"
  fi
done

Proper Fix

The build system should ensure src/web/active-listener.ts is only bundled into a single shared chunk that both dist/ and plugin-sdk/ import from, so the listeners Map exists exactly once. Alternatively, the state could be moved to a runtime singleton (e.g., on globalThis or a shared service registry).

Environment

  • OpenClaw: 2026.3.13 (also confirmed on 2026.3.12)
  • OS: Linux 6.8.0-100-generic (x64)
  • Node: v22.22.0
  • Channel: WhatsApp (whatsapp-web.js)

Related

  • #30177 (same error, filed 2026-02-26, still open)

extent analysis

Fix Plan

To resolve the issue, we need to ensure that the listeners Map is shared across all chunks. Here are the steps:

  • Short-term fix: Apply the provided one-liner patch to replace the module-scoped Map with a globalThis-shared Map in all affected chunks:
cd /usr/lib/node_modules/openclaw/dist
for f in *.js plugin-sdk/*.js; do
  LINENUM=$(grep -n '#region src/web/active-listener.ts' "$f" 2>/dev/null | head -1 | cut -d: -f1)
  if [ -n "$LINENUM" ]; then
    MAPLINE=$((LINENUM + 1))
    sed -i "${MAPLINE}s|const listeners = /\* @__PURE__ \*/ new Map();|const listeners = globalThis.__oc_wa_listeners \?\?= new Map();|" "$f"
  fi
done
  • Proper fix: Modify the build system to bundle src/web/active-listener.ts into a single shared chunk that both dist/ and plugin-sdk/ import from. Alternatively, move the state to a runtime singleton (e.g., on globalThis or a shared service registry).

Example code for the proper fix:

// Create a shared service registry
const registry = globalThis.__oc_wa_registry ??= {};

// Initialize the listeners Map in the registry
registry.listeners = registry.listeners ?? new Map();

// Use the shared listeners Map
const listeners = registry.listeners;

Verification

After applying the fix, verify that proactive sends work as expected:

  1. Restart the gateway with WhatsApp configured.
  2. Send an inbound message → auto-reply should work.
  3. Use the message tool to proactively send → should succeed without errors.
  4. Use CLI openclaw message send --channel whatsapp → should succeed without errors.

Extra Tips

  • To prevent similar issues in the future, ensure that shared state is properly managed across chunks and modules.
  • Consider using a build system that supports tree shaking and code splitting to minimize the number of chunks and reduce the likelihood of duplicated modules.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING