openclaw - ✅(Solved) Fix WhatsApp pairing: verification message sent without inbound text message [4 pull requests, 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#73797Fetched 2026-04-29 06:15:02
View on GitHub
Comments
1
Participants
2
Timeline
14
Reactions
0
Author
Timeline (top)
referenced ×8cross-referenced ×4closed ×1commented ×1

Fix Action

Fixed

PR fix notes

PR #73810: fix(whatsapp): gate pairing access-control on extractable inbound user content (#73797)

Description (problem / solution / changelog)

Fixes #73797.

Problem

`checkInboundAccessControl` runs for every `messages.upsert` notify entry. With `dmPolicy: pairing`, that includes receipts, typing indicators, presence updates, contact-sync events, and protocol messages — so the gateway can send a pairing verification reply to a peer who never typed anything (e.g. when Master sends an outbound message to a new JID and the receipt round-trip arrives back before the recipient ever replies).

The existing `fromMe` recent-outbound-echo guard catches the bot's own outbound echoes but does not catch inbound non-content events that Baileys delivers through the same upsert stream.

Fix

Add a fast `hasInboundUserContent(message)` helper in `extensions/whatsapp/src/inbound/extract.ts` that returns true iff at least one of the existing extractors would surface user-visible content:

SourceReturns true on
`extractText``conversation`, `extendedTextMessage.text`, image/video/document captions, contact placeholder
`extractMediaPlaceholder`image/video/audio/document/sticker mime types
`extractContactContext``contactMessage`, `contactsArrayMessage`
`extractLocationData``locationMessage`/`liveLocationMessage` with valid coords

Call it at the top of `normalizeInboundMessage` right after the existing `fromMe` recent-outbound-echo guard, before `checkInboundAccessControl`. Non-content events bail out cleanly with no pairing side effects, no read-receipt, no enqueue.

All four extractors are pure object-tree walks (no I/O, no allocation beyond the existing message wrapping), so the gate adds only microseconds per upsert event.

Why this shape over alternatives

  • vs. "do the body extraction in normalizeInboundMessage and pass it through" — would require restructuring the existing `enrichInboundMessage` (which currently runs after `normalizeInboundMessage` and includes media download). Splitting body-only extraction from media download is invasive.
  • vs. "suppress pairing reply when an outbound was sent to this JID recently" — proposed by the user, but `fromMe` already covers tracked outbound echoes; the missing case is non-content notify events (typing, receipts) on the recipient side, which a recent-outbound check wouldn't help with anyway.
  • vs. "pass an explicit no-pairing classification" — clawsweeper's alternative, equivalent in effect to this gate. The content-presence check is the simpler form.

Tests

Added `describe("hasInboundUserContent")` block in `extract.test.ts` with 15 cases:

  • 10 positive cases covering each supported content type (text, extended text, image, video, audio, document, sticker, location, live location, contact)
  • 5 negative cases covering the regression: `undefined`, empty object, protocol-only message, senderKey-distribution-only message, location with missing coords, and empty/whitespace-only text

``` pnpm vitest run --config test/vitest/vitest.extension-whatsapp.config.ts extensions/whatsapp/src/inbound/extract.test.ts → 23 passed (8 existing + 15 new)

pnpm vitest run --config test/vitest/vitest.extension-whatsapp.config.ts → 641 passed (full WhatsApp ext suite, no regressions) ```

🦞 lobster-biscuit


Sign-Off: hclsys

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/whatsapp/src/inbound/extract.test.ts (modified, +111/-1)
  • extensions/whatsapp/src/inbound/extract.ts (modified, +25/-0)
  • extensions/whatsapp/src/inbound/monitor.ts (modified, +13/-0)

PR #73823: fix(whatsapp): gate pairing access-control on extractable inbound user content (#73797)

Description (problem / solution / changelog)

Fixes #73797. Re-submission of #73810 (auto-closed by openclaw-barnacle for queue cap).

Includes the original fix plus the greptile review follow-up from the closed PR (button/list/interactive response coverage).

Problem

`checkInboundAccessControl` runs for every `messages.upsert` notify entry. With `dmPolicy: pairing`, that includes receipts, typing indicators, presence updates, contact-sync events, and protocol messages — so the gateway can send a pairing verification reply to a peer who never typed anything (e.g. when Master sends an outbound message to a new JID and the receipt round-trip arrives back before the recipient ever replies).

Fix

Add a fast `hasInboundUserContent(message)` helper in `extensions/whatsapp/src/inbound/extract.ts` that returns true iff at least one of the existing extractors would surface user-visible content OR the message is an explicit user button/list/interactive selection.

SourceReturns true on
`extractText``conversation`, `extendedTextMessage.text`, image/video/document captions, contact placeholder
`extractMediaPlaceholder`image/video/audio/document/sticker mime types
`extractContactContext``contactMessage`, `contactsArrayMessage`
`extractLocationData``locationMessage`/`liveLocationMessage` with valid coords
`hasInteractiveResponseContent` (new helper)`buttonsResponseMessage`, `listResponseMessage`, `templateButtonReplyMessage`, `interactiveResponseMessage` (root or wrapped via `buildMessageChain`)

Call it at the top of `normalizeInboundMessage` right after the existing `fromMe` recent-outbound-echo guard, before `checkInboundAccessControl`. Non-content events bail out cleanly with no pairing side effects, no read-receipt, no enqueue.

All checks are pure object-tree walks (no I/O, no allocation beyond the existing message wrapping), so the gate adds only microseconds per upsert event.

Tests

Added `describe("hasInboundUserContent")` block in `extract.test.ts` with 20 cases:

  • 15 positive cases covering each supported content type (text, extended text, image, video, audio, document, sticker, location, live location, contact, buttons response, list response, template button reply, interactive response, ephemeral-wrapped buttons response)
  • 5 negative cases covering the regression: `undefined`, empty object, protocol-only message, senderKey-distribution-only message, location with missing coords, and empty/whitespace-only text

``` pnpm vitest run --config test/vitest/vitest.extension-whatsapp.config.ts \ extensions/whatsapp/src/inbound/extract.test.ts → 28 passed (8 existing + 20 new)

pnpm vitest run --config test/vitest/vitest.extension-whatsapp.config.ts → 641 passed (full WhatsApp ext suite, no regressions) ```

Why this shape over alternatives

  • vs. "do the body extraction in normalizeInboundMessage and pass it through" — would require restructuring the existing `enrichInboundMessage` (which currently runs after `normalizeInboundMessage` and includes media download). Splitting body-only extraction from media download is invasive.
  • vs. "suppress pairing reply when an outbound was sent to this JID recently" — proposed by the user, but `fromMe` already covers tracked outbound echoes; the missing case is non-content notify events (typing, receipts) on the recipient side, which a recent-outbound check wouldn't help with anyway.
  • vs. "pass an explicit no-pairing classification" — clawsweeper's alternative, equivalent in effect to this gate.

🦞 lobster-biscuit


Sign-Off: hclsys

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • extensions/whatsapp/src/inbound/extract.test.ts (modified, +180/-1)
  • extensions/whatsapp/src/inbound/extract.ts (modified, +46/-0)
  • extensions/whatsapp/src/inbound/monitor.ts (modified, +13/-0)

PR #73961: feat(whatsapp): support native outbound mentions

Description (problem / solution / changelog)

Summary

  • Add native WhatsApp outbound @mention metadata for group messages.
  • Resolve @+<digits>/@<digits> tokens to the correct WhatsApp JIDs where participant data is available.
  • Keep the replacement narrow to outbound mentions and leave reply-target media injection for a separate scoped follow-up.

Fixes #39879.

Attribution

This carries forward the useful outbound mention approach from @joe2643 in https://github.com/openclaw/openclaw/pull/56863 with credit preserved.

Review notes

  • Strip device suffixes when rewriting LID mention text.
  • Apply mention rewrites by match position so skipped code spans are not modified.
  • Do not include quoted-media thumbnail fallback changes in this narrow PR unless they are fully fixed and tested.

Validation

  • pnpm check:changed

ProjectClownfish replacement details:

  • Cluster: ghcrawl-157000-autonomous-smoke
  • Source PRs: https://github.com/openclaw/openclaw/pull/56863
  • Credit: Credit @joe2643 and source PR https://github.com/openclaw/openclaw/pull/56863 for the original outbound mention implementation approach.; Credit #39879 reporter @kengi1437 for the canonical missing mentionedJids report.; If the proactive send participant-resolution callback idea is used, credit @fridayck's review comment on #56863.
  • Validation: pnpm check:changed

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/channels/whatsapp.md (modified, +1/-0)
  • extensions/whatsapp/src/inbound/monitor.ts (modified, +80/-16)
  • extensions/whatsapp/src/inbound/outbound-mentions.test.ts (added, +102/-0)
  • extensions/whatsapp/src/inbound/outbound-mentions.ts (added, +252/-0)
  • extensions/whatsapp/src/inbound/send-api.test.ts (modified, +52/-0)
  • extensions/whatsapp/src/inbound/send-api.ts (modified, +33/-6)
RAW_BUFFERClick to expand / collapse

Problem

When Master sends an outbound WhatsApp message to a new number (not in allowFrom), the recipient sometimes receives an unsolicited pairing/verification message — even though they never sent a message to Master.

Expected behavior

The pairing verification message should only be sent when someone actively sends a text message to Master. It should never trigger on:

  • Delivery receipts
  • Read receipts
  • Presence updates
  • Typing indicators
  • Contact sync events
  • Any non-text Baileys event

Current behavior

checkInboundAccessControl in extensions/whatsapp/src/inbound/access-control.ts is called for events that are not real inbound text messages. The isFromMe check exists but does not cover all cases where Baileys delivers non-message events as messages.upsert.

Suggested fix

In normalizeInboundMessage (login.ts), before calling checkInboundAccessControl, verify:

  1. The message has actual text/media content (not just a receipt or protocol message)
  2. OR: if Master sent an outbound message to this JID recently (e.g. <1 hour), suppress the pairing reply

Reproduction

  1. Set dmPolicy: pairing
  2. Send an outbound WhatsApp message to a new number not in allowFrom
  3. The recipient receives an unsolicited verification message without sending anything

Environment

  • OpenClaw version: latest (npm)
  • WhatsApp extension with Baileys
  • macOS host

extent analysis

TL;DR

Modify the normalizeInboundMessage function in login.ts to filter out non-text events and recently sent outbound messages before calling checkInboundAccessControl.

Guidance

  • Verify that the normalizeInboundMessage function correctly checks for actual text/media content in the message to prevent triggering on receipts or protocol messages.
  • Implement a check for recent outbound messages to the same JID (e.g., within the last hour) to suppress the pairing reply in such cases.
  • Review the checkInboundAccessControl function in extensions/whatsapp/src/inbound/access-control.ts to ensure it is not being called unnecessarily for non-text events.
  • Test the changes with the reproduction steps provided to ensure the issue is resolved.

Example

// Example of how to check for actual text/media content
if (message.message && (message.message.text || message.message.media)) {
  // Call checkInboundAccessControl
}

// Example of how to check for recent outbound messages
const recentOutboundMessages = []; // Store recent outbound messages
if (recentOutboundMessages.includes(jid) && Date.now() - recentOutboundMessages[jid].timestamp < 3600000) {
  // Suppress pairing reply
}

Notes

The provided fix assumes that the normalizeInboundMessage function has access to the necessary information about the message and the recent outbound messages. Additional modifications may be needed to store and retrieve this information.

Recommendation

Apply the suggested workaround by modifying the normalizeInboundMessage function to filter out non-text events and recently sent outbound messages. This should prevent the unsolicited pairing verification messages from being sent.

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

The pairing verification message should only be sent when someone actively sends a text message to Master. It should never trigger on:

  • Delivery receipts
  • Read receipts
  • Presence updates
  • Typing indicators
  • Contact sync events
  • Any non-text Baileys event

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 - ✅(Solved) Fix WhatsApp pairing: verification message sent without inbound text message [4 pull requests, 1 comments, 2 participants]