fix: do not resend an empty message when a retry receipt cannot be answered - #2728
Open
iagovelasco3 wants to merge 1 commit into
Open
iagovelasco3 wants to merge 1 commit into
iagovelasco3 wants to merge 1 commit into
Conversation
…swered
`getMessage` returned `{ conversation: '' }` whenever the message row was not
found or the lookup threw. Baileys only skips a retry resend when `getMessage`
resolves to a falsy value, so returning that object makes `sendMessagesAgain`
relay an EMPTY message reusing the original message id. Group members then see
a bubble with a timestamp and no content, repeated on every retry attempt
(maxMsgRetryCount per requesting participant).
It is easy to hit in groups: any member that fails to decrypt asks for a retry,
and by then the original message may be gone from the DB (retention, instance
recreated, or a message this instance never persisted).
Measured on a 11-cell deployment before the fix: ~17.7k empty bubbles in 24h.
After returning `undefined` instead, 10 of 11 cells dropped to zero, with the
remainder coming from a third-party sender outside the deployment. No change in
delivery of real messages.
Also guards `webMessageInfo[0]` with optional chaining, so an empty result set no
longer throws on `.message`, and reuses the local `message` variable in the poll
branch. The poll and event decrypt callers in Baileys already guard with
`if (msg)`, so they behave correctly with `undefined` too.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Contributor
Reviewer's GuideFixes retry handling so unavailable messages are skipped instead of being resent as empty bubbles, while retaining existing poll handling and improving lookup safety and diagnostics. Sequence diagram for skipping unavailable WhatsApp retry messagessequenceDiagram
participant Baileys
participant GetMessage as getMessage
participant MessageDB as Message lookup
participant Logger
participant Chat as Chat recipients
Baileys->>GetMessage: getMessage(key, full)
GetMessage->>MessageDB: find message by key
MessageDB-->>GetMessage: message or empty/error
alt message found
GetMessage-->>Baileys: message
Baileys->>Chat: resend original message
else message unavailable or lookup fails
GetMessage->>Logger: debug unavailable message
GetMessage-->>Baileys: undefined
Baileys-->>Chat: skip retry resend
end
Flow diagram for safe retry message lookupflowchart TD
A[getMessage] --> B[Lookup message]
B --> C{message exists?}
C -->|yes| D{pollCreationMessage?}
D -->|yes| E[Restore messageSecret and return poll message]
D -->|no| F[Return message]
C -->|no or lookup error| G[Log diagnostic]
G --> H[Return undefined]
H --> I[Baileys skips resend]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📋 Description
getMessagereturns{ conversation: '' }when the message row is not found or the lookup throws.Baileys only skips a retry resend when
getMessageresolves to a falsy value (sendMessagesAgaininSocket/messages-recv,if (msg && await willSendMessageAgain(...))). Returning an object makes it relay an empty message reusing the original message id, so every member of the chat receives a bubble with a timestamp and no content. It repeats on each retry attempt, up tomaxMsgRetryCountper requesting participant.This is easy to hit in groups: any member that fails to decrypt (
No matching sessions found,Received message with old counter) asks for a retry, and by then the original message may no longer be in theMessagetable (retention, instance recreated, or a message this instance never persisted).This PR returns
undefinedinstead, in both the not-found and the error path. Baileys then logsrecv retry request, but message not availableand sends nothing, which is the correct behavior.Two smaller changes in the same method:
webMessageInfo[0]is now optional-chained, so an empty result set no longer throws on.messageand falls through to thecatch.messagevariable instead of re-readingwebMessageInfo[0].messagethree times.The other consumers of
getMessagein Baileys (poll vote decrypt and event response decrypt inprocess-message) already guard withif (msg), so they also behave correctly withundefined. Today they receive the truthy{ conversation: '' }, proceed, and fail later on a missingmessageSecret.🔗 Related Issue
No open issue; found while investigating empty bubbles reported by end users.
🧪 Type of Change
🧪 Testing
Verified in production on a deployment of 11 Evolution instances (~480 connected sessions, group-heavy traffic).
Before: ~17,700 empty bubbles persisted in 24h across the deployment, concentrated in the busiest group senders. Traced by finding the same message id re-arriving with
{"conversation": ""}after having arrived earlier with real content, for example animageMessagesent at 12:21 UTC that came back at 12:38 and 12:39 as an empty conversation. 619 of 623 empty ids in a 24h sample never existed with content anywhere in the database.After: 10 of the 11 instances went to exactly zero empty messages with
fromMe, holding over a 40 minute window with ~3,000 retries answered withundefinedin that period. The remaining instance only receives them, from a third-party sender outside the deployment. Real message delivery was unchanged: the two campaign accounts under investigation sent 276 and 240 messages after the change with no empty ones and no new errors.tsc --noEmit,eslintandprettier --checkare clean on the changed file.📝 Additional Notes
The same defect is present on
main. I targeteddevelopper CONTRIBUTING.md; happy to open a second PR againstmainif you prefer.The trade-off is explicit: a recipient that asks for a retry of a message we no longer have will not receive that message. That is already the outcome today, except the recipient also gets a confusing empty bubble.
🤖 Generated with Claude Code
Summary by Sourcery
Skip WhatsApp retry resends when the original message cannot be retrieved instead of sending empty message bubbles.
Bug Fixes:
Enhancements: