Skip to content

fix(whatsapp): normalize chatId to JID before sendMessage - #40182

Closed
travelsim wants to merge 1 commit into
NousResearch:mainfrom
travelsim:fix/whatsapp-normalize-chatid
Closed

fix(whatsapp): normalize chatId to JID before sendMessage#40182
travelsim wants to merge 1 commit into
NousResearch:mainfrom
travelsim:fix/whatsapp-normalize-chatid

Conversation

@travelsim

Copy link
Copy Markdown

What

The WhatsApp bridge at scripts/whatsapp-bridge/bridge.js crashes with HTTP 500 on every outbound hermes send when the chatId is a URI/E.164/digits form (e.g. whatsapp:+61XXXXXXXXX, +61XXXXXXXXX, or 61XXXXXXXXX). The bridge passes the value straight to Baileys' sock.sendMessage(), which calls jidDecode() internally and throws:

Cannot destructure property 'user' of 'jidDecode(...)' as it is undefined.

This affects every first-time user of the WhatsApp gateway: the channel directory is only populated by inbound messages, so hermes send and the hermes acp HTTP path are broken until at least one inbound message arrives.

Fix

Add a normalizeChatId(raw) helper to the bridge and call it on the chatId in the 4 affected HTTP handlers:

  • POST /send — convert to JID before each chunk
  • POST /edit — convert to JID for both the edit key and the follow-up chunks
  • POST /send-media — convert before sendMessage
  • POST /typing — convert before sendPresenceUpdate

The normalizer:

  • leaves anything containing @ (valid JID, group/lid/newsletter) untouched
  • strips a leading whatsapp: URI scheme
  • strips a leading +
  • strips non-digit characters
  • returns ${digits}@s.whatsapp.net (gives up and returns the original on fewer than 5 digits, letting Baileys' own error path fire)

Tested

  • POST /send with {chatId: 'whatsapp:+61XXXXXXXXX', message: '...'} returns {success:true, messageId:'3EB07...'} (delivered to the user's phone).
  • POST /send with already-valid JIDs still works.
  • POST /send with malformed inputs (< 5 digits) falls through to the original error path.
  • POST /typing and POST /edit and POST /send-media also use the normalizer.

Context

This is a 33-line / 5-call-site change in one file. No new dependencies, no API changes for the gateway, no behavior change for already-valid JIDs. The bridge is a separate process from the gateway; restarting the bridge loads the new code.

Baileys' sendMessage() calls jidDecode() internally, which throws
'Cannot destructure user of jidDecode(...) as it is undefined' when
the chatId is not a valid JID. The gateway and `hermes send`
legitimately pass URI/E.164 forms like 'whatsapp:+61XXXXXXXXX',
'+61XXXXXXXXX', or '61XXXXXXXXX' (especially before the channel
directory has been populated by inbound messages), so the bridge
crashed with 500 on every outbound send.

Add normalizeChatId() to the bridge and wire it into /send, /edit,
/send-media, and /typing so any input form is converted to a valid
<digits>@s.whatsapp.net JID before reaching Baileys. Already-valid
JIDs (with @server suffix) and group/lid/newsletter JIDs pass
through unchanged.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: Approved

This PR fixes a real WhatsApp send failure by normalizing non-JID chatId values (E.164, URI-prefixed, bare digits) before every Baileys sendMessage/sendPresenceUpdate call.

Changes

  • Introduced normalizeChatId(raw) with clear heredoc-style documentation of accepted formats and exit conditions.
  • Applied uniformly across all four write paths: /send, /edit, /send-media, /typing.
  • Non-JID inputs are normalized exactly once per request (targetJid) and reused for all chunks — no redundant work.

Looks Good

  • Handlingincludes('@') short-circuit correctly escapes group (@g.us), LID (@lid), and newsletter (@newsletter) identifiers untouched.
  • Guard raildigits.length < 5 returns raw so Baileys produces a real error rather than a silent misroute; sensible safety net without hiding failures.
  • Scope — only /scripts/whatsapp-bridge/bridge.js changed (33 additions / 6 deletions); no API surface shift, no config changes.
  • Consistency — every caller path in the file is covered; no path was accidentally skipped.

Reviewed by Hermes Agent

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter labels Jun 6, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Fixed on main via #50379 (salvage of #8639) — bare WhatsApp phone targets are now normalized to <digits>@s.whatsapp.net before the bridge call at every outbound send site.

@sgaofen's #8639 was the earliest of this cluster, so it was used as the base and credited first; thanks @travelsim for independently catching and fixing the same bug. Closing as duplicate.

@teknium1 teknium1 closed this Jun 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Medium — degraded but workaround exists platform/whatsapp WhatsApp Business adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants