sendSeen returns success but does not reliably clear unread state #1

Open
opened 2026-08-23 18:46:12 +02:00 by eddy · 1 comment
Owner

Context

Our production uses the patched whatsapp-web.js package in the custom wwebjs-api container. The active WWeb version is 2.3000.1045821369.

Observed behavior

For chat 120363407613652082@g.us in session Eddy, sendSeen(chatId) returns HTTP 200 and {\"success\":true,\"result\":true}, but the chat can remain unread (unreadCount: 1). A subsequent manual sendSeen or opening the chat may clear it.

Our webhook workflow receives a message event and calls sendSeen; logs report success, but querying the Eddy chat immediately afterwards still reports the new message as unread. This is reproducible with multiple messages, not only one incident.

Current implementation

window.WWebJS.sendSeen currently calls:

await window.require('WAWebUpdateUnreadChatAction').sendSeen({
  chat,
  threadId: undefined
});
return true;

The function returns true after the internal call completes, without verifying that the unread state changed.

Expected behavior

client.sendSeen(chatId) should reliably mark the chat as seen, or return a failure when the read state cannot be applied. The return value should not report success merely because the internal call did not throw.

Requested investigation

Please test the active WWeb version and determine whether the correct internal call is sendSeen({ chat, threadId: undefined }), markSeen(chat), or another version-specific action. Please also ensure Stream.markUnavailable() is executed in a finally block and consider a functional confirmation of the resulting unread state.

No changes have been made to this repository as part of this report.

## Context Our production uses the patched `whatsapp-web.js` package in the custom `wwebjs-api` container. The active WWeb version is `2.3000.1045821369`. ## Observed behavior For chat `120363407613652082@g.us` in session `Eddy`, `sendSeen(chatId)` returns HTTP 200 and `{\"success\":true,\"result\":true}`, but the chat can remain unread (`unreadCount: 1`). A subsequent manual `sendSeen` or opening the chat may clear it. Our webhook workflow receives a message event and calls `sendSeen`; logs report success, but querying the Eddy chat immediately afterwards still reports the new message as unread. This is reproducible with multiple messages, not only one incident. ## Current implementation `window.WWebJS.sendSeen` currently calls: ```js await window.require('WAWebUpdateUnreadChatAction').sendSeen({ chat, threadId: undefined }); return true; ``` The function returns `true` after the internal call completes, without verifying that the unread state changed. ## Expected behavior `client.sendSeen(chatId)` should reliably mark the chat as seen, or return a failure when the read state cannot be applied. The return value should not report success merely because the internal call did not throw. ## Related upstream reports - https://github.com/wwebjs/whatsapp-web.js/issues/5718 - https://github.com/wwebjs/whatsapp-web.js/issues/5730 - https://github.com/wwebjs/whatsapp-web.js/pull/5719 - https://github.com/wwebjs/whatsapp-web.js/pull/5729 - https://github.com/wwebjs/whatsapp-web.js/pull/5737 ## Requested investigation Please test the active WWeb version and determine whether the correct internal call is `sendSeen({ chat, threadId: undefined })`, `markSeen(chat)`, or another version-specific action. Please also ensure `Stream.markUnavailable()` is executed in a `finally` block and consider a functional confirmation of the resulting unread state. No changes have been made to this repository as part of this report.
Author
Owner

I investigated the current implementation and the related upstream reports.

The active code already uses the newer WhatsApp Web call from upstream PR #5729:
WAWebUpdateUnreadChatAction.sendSeen({ chat, threadId: undefined }).
This is also the form currently present on upstream main, so replacing it with markSeen(chat) (PR #5719) is not a complete solution. Community reports on #5719 indicate that it avoids the markedUnread exception but can fail to produce blue read receipts.

Relevant upstream status:

  • #5718: original markedUnread regression; closed.
  • #5719: markSeen(chat) workaround; merged, but reports mention missing blue ticks.
  • #5729: object-signature fix with chat and threadId; merged on 2026-01-29 and already present here.
  • #5737: adds the new signature and cleanup, but is closed and unmerged. Its broad TypeError handling should not be copied because it can report success for a real failure.

The remaining difference relevant to this issue is that the local helper returns true after the internal promise resolves, without checking whether unreadCount actually became zero. It also calls Stream.markUnavailable() only on the success path. A sensible next patch is therefore to keep the current object-signature, move stream cleanup into finally, and return true only after a bounded refresh/polling loop confirms unreadCount === 0; otherwise return false or propagate a real internal error. This should preserve read receipts better than a silent markSeen fallback.

I have not yet validated this against the live production session, so the postcondition and timing still need testing with WWeb 2.3000.1045821369. Please share whether the refreshed chat model reports unreadCount === 0 after the current internal call, and whether the unread event arrives later; that will distinguish an action failure from an asynchronous/stale-model observation.

I investigated the current implementation and the related upstream reports. The active code already uses the newer WhatsApp Web call from upstream PR #5729: `WAWebUpdateUnreadChatAction.sendSeen({ chat, threadId: undefined })`. This is also the form currently present on upstream `main`, so replacing it with `markSeen(chat)` (PR #5719) is not a complete solution. Community reports on #5719 indicate that it avoids the `markedUnread` exception but can fail to produce blue read receipts. Relevant upstream status: - #5718: original `markedUnread` regression; closed. - #5719: `markSeen(chat)` workaround; merged, but reports mention missing blue ticks. - #5729: object-signature fix with `chat` and `threadId`; merged on 2026-01-29 and already present here. - #5737: adds the new signature and cleanup, but is closed and unmerged. Its broad `TypeError` handling should not be copied because it can report success for a real failure. The remaining difference relevant to this issue is that the local helper returns `true` after the internal promise resolves, without checking whether `unreadCount` actually became zero. It also calls `Stream.markUnavailable()` only on the success path. A sensible next patch is therefore to keep the current object-signature, move stream cleanup into `finally`, and return `true` only after a bounded refresh/polling loop confirms `unreadCount === 0`; otherwise return `false` or propagate a real internal error. This should preserve read receipts better than a silent `markSeen` fallback. I have not yet validated this against the live production session, so the postcondition and timing still need testing with WWeb `2.3000.1045821369`. Please share whether the refreshed chat model reports `unreadCount === 0` after the current internal call, and whether the unread event arrives later; that will distinguish an action failure from an asynchronous/stale-model observation.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eddy/whatsapp-web.js#1
No description provided.