Skip to content

fix(baileys): correct JID filter in markMessageAsRead for all user types#2418

Open
easedu wants to merge 1 commit intoEvolutionAPI:mainfrom
easedu:fix/mark-message-as-read-jid-filter
Open

fix(baileys): correct JID filter in markMessageAsRead for all user types#2418
easedu wants to merge 1 commit intoEvolutionAPI:mainfrom
easedu:fix/mark-message-as-read-jid-filter

Conversation

@easedu
Copy link

@easedu easedu commented Feb 7, 2026

📋 Description

markMessageAsRead silently fails for most users due to an incorrect JID filter condition.

The current code uses an allowlist approach:

if (isJidGroup(read.remoteJid) || isPnUser(read.remoteJid)) {

isPnUser() only matches @pn.whatsapp.net JIDs, which excludes:

  • Normal users (@s.whatsapp.net) — the vast majority of contacts
  • LID users (@lid) — linked device identifiers

Messages were never actually marked as read for these JID types.

Root cause: When Baileys upgraded to v7, isJidUser() (which matched both @s.whatsapp.net and @pn.whatsapp.net) was removed. The replacement with isPnUser() was incorrect as it only covers one of the two user JID formats.

Solution: Replace the allowlist with a denylist that excludes only JID types that genuinely cannot receive read receipts:

if (!isJidBroadcast(read.remoteJid) && !isJidNewsletter(read.remoteJid)) {

A denylist is more resilient to future Baileys changes — new JID types will work by default instead of being silently excluded.

🔗 Related Issue

Closes #2277

🧪 Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 📚 Documentation update
  • 🔧 Refactoring (no functional changes)
  • ⚡ Performance improvement
  • 🧹 Code cleanup
  • 🔒 Security fix

🧪 Testing

  • Manual testing completed
  • Functionality verified in development environment
  • No breaking changes introduced
  • Tested with different connection types (if applicable)

Verified that isJidBroadcast and isJidNewsletter correctly exclude only @broadcast and @newsletter JIDs, while allowing @s.whatsapp.net, @pn.whatsapp.net, @lid, and group JIDs to pass through.

📸 Screenshots (if applicable)

N/A — backend logic change only.

✅ Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have manually tested my changes thoroughly
  • I have verified the changes work with different scenarios
  • Any dependent changes have been merged and published

📝 Additional Notes

Both isJidBroadcast and isJidNewsletter were already imported in the file (used elsewhere in the codebase), so no new dependencies were added.

Summary by Sourcery

Bug Fixes:

  • Correct markMessageAsRead JID filtering so that all non-broadcast, non-newsletter JIDs (including normal, PN, LID, and group users) are eligible to be marked as read.

The condition used isPnUser() which only matches @pn.whatsapp.net JIDs,
silently excluding normal users (@s.whatsapp.net) and LID users (@lid).
Messages were never actually marked as read for these JID types.

Replace allowlist approach (isJidGroup || isPnUser) with denylist
(!isJidBroadcast && !isJidNewsletter) to correctly include all valid
user and group JIDs while excluding only broadcast and newsletter JIDs.

Fixes EvolutionAPI#2277
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 7, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adjusts the JID filter in Baileys markMessageAsRead to a denylist that excludes only broadcast and newsletter JIDs so read receipts are correctly sent for all valid user and group JID types.

Sequence diagram for updated markMessageAsRead JID filtering

sequenceDiagram
    actor User
    participant App
    participant BaileysStartupService
    participant BaileysSocket

    User->>App: Trigger markMessageAsRead
    App->>BaileysStartupService: markMessageAsRead(data)
    loop for each read in data.readMessages
        BaileysStartupService->>BaileysStartupService: isJidBroadcast(read.remoteJid)
        alt remoteJid is broadcast
            BaileysStartupService-->>BaileysStartupService: Skip read (no key added)
        else remoteJid is not broadcast
            BaileysStartupService->>BaileysStartupService: isJidNewsletter(read.remoteJid)
            alt remoteJid is newsletter
                BaileysStartupService-->>BaileysStartupService: Skip read (no key added)
            else remoteJid is not newsletter
                BaileysStartupService-->>BaileysStartupService: Push message key to keys
            end
        end
    end
    BaileysStartupService->>BaileysSocket: sendReadReceipt(keys)
    BaileysSocket-->>BaileysStartupService: Read receipts acknowledged
    BaileysStartupService-->>App: markMessageAsRead completed
    App-->>User: Messages appear as read
Loading

Class diagram for BaileysStartupService markMessageAsRead JID filtering

classDiagram
    class BaileysStartupService {
        +markMessageAsRead(data)
        -buildReadKeys(data) keys
    }

    class JidPredicates {
        +isJidBroadcast(jid)
        +isJidNewsletter(jid)
    }

    class ReadMessage {
        +remoteJid
        +fromMe
        +id
    }

    class ReadData {
        +readMessages ReadMessage[]
    }

    BaileysStartupService --> ReadData : uses
    ReadData --> ReadMessage : contains
    BaileysStartupService ..> JidPredicates : filters JIDs with
    JidPredicates <.. ReadMessage : evaluates remoteJid
Loading

Flow diagram for new JID denylist in markMessageAsRead

flowchart TD
    A[Start processing readMessages] --> B[Take next read from data.readMessages]
    B --> C{Any read left?}
    C -->|No| Z[End]
    C -->|Yes| D["Evaluate isJidBroadcast(read.remoteJid)"]
    D --> E{is broadcast?}
    E -->|Yes| B
    E -->|No| F["Evaluate isJidNewsletter(read.remoteJid)"]
    F --> G{is newsletter?}
    G -->|Yes| B
    G -->|No| H["Push {remoteJid, fromMe, id} into keys"]
    H --> B
Loading

File-Level Changes

Change Details Files
Relax JID filtering in markMessageAsRead to include all non-broadcast, non-newsletter JIDs.
  • Replaced the allowlist condition using isJidGroup or isPnUser with a denylist that excludes isJidBroadcast and isJidNewsletter JIDs.
  • Ensured that message keys are now generated for all eligible JID types (normal users, PN users, LID users, and groups) while still skipping broadcast/newsletter JIDs.
src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#2277 Fix the runtime TypeError related to JID checking in POST /chat/markMessageAsRead so that it no longer throws a 500 error.
#2277 Ensure that POST /chat/markMessageAsRead correctly marks messages as read for standard WhatsApp users (e.g., @s.whatsapp.net) and other valid JID types.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • Since the logic change from an allowlist to a denylist is subtle and driven by Baileys JID-type behavior, consider adding a brief inline comment explaining why only broadcast and newsletter JIDs are excluded to help future maintainers avoid reintroducing overly restrictive filters.
  • If read.remoteJid can ever be null/undefined or malformed from upstream data, it may be worth guarding or asserting before passing it into isJidBroadcast/isJidNewsletter to avoid unexpected runtime issues.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Since the logic change from an allowlist to a denylist is subtle and driven by Baileys JID-type behavior, consider adding a brief inline comment explaining why only broadcast and newsletter JIDs are excluded to help future maintainers avoid reintroducing overly restrictive filters.
- If `read.remoteJid` can ever be null/undefined or malformed from upstream data, it may be worth guarding or asserting before passing it into `isJidBroadcast`/`isJidNewsletter` to avoid unexpected runtime issues.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

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.

[BUG] TypeError: (0 , D.isJidUser) is not a function ao marcar mensagens como lida

1 participant