Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
fix: do not resend an empty message when a retry receipt cannot be an…
…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) <noreply@anthropic.com>
  • Loading branch information
Iago Velasco and claude committed Sep 14, 2026
commit 1c4160526256927f6d94f812e2e680d3e96a623a
26 changes: 20 additions & 6 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,24 +656,38 @@ export class BaileysStartupService extends ChannelStartupService {
if (full) {
return webMessageInfo[0];
}
if (webMessageInfo[0].message?.pollCreationMessage) {
const messageSecretBase64 = webMessageInfo[0].message?.messageContextInfo?.messageSecret;

// Baileys only skips a retry resend when getMessage resolves to a falsy value. Returning an
// object here (for example `{ conversation: '' }`) makes it relay an EMPTY message reusing the
// original message id, which shows up in the chat as a bubble with a timestamp and no content.
const message = webMessageInfo[0]?.message;

if (!message) {
this.logger.debug(`getMessage: message ${key.id} not found, skipping retry resend`);

return undefined;
}

if (message.pollCreationMessage) {
const messageSecretBase64 = message.messageContextInfo?.messageSecret;

if (typeof messageSecretBase64 === 'string') {
const messageSecret = Buffer.from(messageSecretBase64, 'base64');

const msg = {
messageContextInfo: { messageSecret },
pollCreationMessage: webMessageInfo[0].message?.pollCreationMessage,
pollCreationMessage: message.pollCreationMessage,
};

return msg;
}
}

return webMessageInfo[0].message;
} catch {
return { conversation: '' };
return message;
} catch (error) {
this.logger.debug(`getMessage: lookup for ${key.id} failed, skipping retry resend: ${error?.toString()}`);

return undefined;
}
}

Expand Down