Skip to content

[BUG] Cloud API: a single unknown wamid in a statuses batch silently drops the whole batch (return instead of continue) #2700

Description

@Satsuj1n

Summary

In messageHandle, the loop over received.statuses uses return where it should use continue. WhatsApp Cloud API delivers status updates in batches, so a single item that fails a guard aborts the remaining items in the same webhook payload — silently, with a 200 returned to Meta.

This is distinct from #2573 (the contacts[0].profile.name TypeError). That one prevents the block from being reached at all; this one drops siblings once it is reached. It survives on develop, so it is not fixed by #2514.

Affected code

src/api/integrations/channel/meta/whatsapp.business.service.ts

if (received.statuses) {
  for await (const item of received.statuses) {
    const key = { id: item.id, remoteJid: this.phoneNumber, fromMe: ... };

    if (settings?.groups_ignore && key.remoteJid.includes('@g.us')) {
      return;            // <-- aborts the whole batch
    }
    if (key.remoteJid !== 'status@broadcast' && !key?.remoteJid?.match(/(:\d+)/)) {
      const findMessage = await this.prismaRepository.message.findFirst({ ... });

      if (!findMessage) {
        return;          // <-- aborts the whole batch
      }

Why it matters in practice

The !findMessage case is not exotic — it happens for any message this Evolution instance did not send itself:

  • messages sent directly through the Graph API
  • messages sent from the WhatsApp Business app on the phone
  • messages predating the instance, or sent while the DB was being migrated

Meta commonly batches statuses for several messages into one payload. One such
"foreign" wamid arriving first means every other message in that batch never
gets its MESSAGES_UPDATE and never gets a MessageUpdate row. Delivery and
read receipts go missing for messages that Evolution does own, with nothing
in the logs to explain it.

Expected

Skip the offending item, keep processing the rest of the batch.

Suggested fix

-            return;
+            continue;

in both places inside the for await (const item of received.statuses) loop.

Note that continue is also the correct semantics for groups_ignore: the
setting means "ignore group messages", not "stop processing this payload".

Versions checked

version affected
v2.3.7 (latest stable) yes
main yes
v2.4.0-rc2 yes
develop yes — #2514 does not touch this loop

Environment

  • Evolution API 2.3.7, WHATSAPP-BUSINESS (Cloud API) integration
  • Node v20.20.0, PostgreSQL, PM2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions