Skip to content

fix: report the missing message instead of failing a delivered send - #151

Open
DenysSamoiliuk wants to merge 2 commits into
avoylenko:mainfrom
DenysSamoiliuk:fix/sendmessage-contract
Open

fix: report the missing message instead of failing a delivered send#151
DenysSamoiliuk wants to merge 2 commits into
avoylenko:mainfrom
DenysSamoiliuk:fix/sendmessage-contract

Conversation

@DenysSamoiliuk

@DenysSamoiliuk DenysSamoiliuk commented Aug 19, 2026

Copy link
Copy Markdown

The problem

POST /client/sendMessage can answer {"success": true} with no message at all. Callers keep the returned id to correlate the message later, so a success with nothing in it leaves them dereferencing undefined.

Why this is not a 500

My first version of this PR answered 500. That was wrong, and worth spelling out.

window.WWebJS.sendMessage hands the message to the chat and awaits it, and only then looks it up — src/util/Injected/Utils.js:

const [msgPromise, sendMsgResultPromise] = window
  .require('WAWebSendMsgChatAction')
  .addAndSendMsgToChat(chat, message);
await msgPromise;                                  // the message is already on its way

return window.require('WAWebCollections')
  .Msg.get(newMsgKey._serialized);                 // and only now do we look for it

So a lookup that misses says nothing about whether the contact received the message. On WhatsApp Web 2.3000.x, which no longer exposes _serialized on MsgKey, it misses on every send — and a caller that retries on 500 delivers a second copy of something the contact already has.

What it does now

Answer 200 with an explicit message: null and a warning:

{
  "success": true,
  "message": null,
  "warning": "whatsapp-web.js did not return the sent message; it may still have been delivered"
}

The caller still learns it has no id to correlate on — which is what the silent {"success": true} failed to tell it — but nothing invites a retry. Same treatment in channelController.

logMissingMessage stays. It reports what the page looked like at that moment — whether a fresh MsgKey exposes _serialized, and how the keys of the messages already in the chat are shaped. That is what identified the cause in the first place.

Related

#150 removes the common cause: the _serialized getter it installs is what makes that Msg.get hit, and it now survives page reloads. With that in, this path should be rare — but the response shape should still not lie either way.

Testing

npm test  → 43 passed
npx eslint src tests

DenysSamoiliuk and others added 2 commits August 19, 2026 15:07
Callers keep the id from the returned message to correlate it later, so
a success response that carries no message leaves them dereferencing
undefined - the failure surfaces in their code, far from its cause.

whatsapp-web.js returns nothing when it refuses to send the content, an
unsupported content type for a channel or a status, which is a failure
and now reports as one on both the client and the channel endpoint. It
also returns nothing when the message went out but the lookup that
follows the send missed it, so the failure branch logs what the page
looks like at that moment - whether a fresh MsgKey exposes
`_serialized`, and how the keys of the messages already in the chat are
shaped - which is what tells those two cases apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Answering 500 was the wrong call. `WWebJS.sendMessage` hands the message to
the chat and awaits it, and only then looks it up with
`Msg.get(newMsgKey._serialized)` - so a lookup that misses says nothing
about whether the contact got the message. On WhatsApp Web builds that
renamed `_serialized` it misses for every send, and a caller that retries
on 500 delivers a second copy of something the contact already has.

Answer 200 with an explicit `message: null` and a warning instead. The
caller still learns that it has no id to correlate on, which is what the
silent `{success: true}` failed to tell it, but nothing invites a retry.

`logMissingMessage` stays: it is what identified the cause, a page whose
`MsgKey` no longer exposes `_serialized`.
@DenysSamoiliuk DenysSamoiliuk changed the title fix: fail sendMessage instead of answering 200 without a message fix: report the missing message instead of failing a delivered send Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant