Chat skips a block of messages when someone posts while you're catching up on unread history

AI-generated summary

Luna_Mora reported a bug where Chat skips a block of messages when a new message arrives while the user is loading unread history. The issue occurs when the initial page load does not cover all unread messages; scrolling down before the page fully loads causes the list to jump directly to the new live message, leaving the intermediate messages invisible and clearing the unread indicator. This behavior persists across browsers and devices, and reloading the channel is required to view the skipped history in incremental steps.

The reporter provided detailed steps to reproduce the issue and a technical analysis suggesting the root cause. A commit from December 2025 regarding user presence handling routes incoming messages in a way that alters the pagination cursor. Specifically, ChatMessagesLoader#computeNextTargetMessage uses the last non-staged message as the cursor for future pages. If a live message is appended to a partially loaded list, the cursor jumps past the unloaded history. Additionally, a separate older bug in #sendNewMessage stages messages at the end of the list without loading the latest page when canLoadMoreFuture is true, causing similar skips when replying to older messages.

In response, awesomerobot confirmed the bug by reproducing it using the provided steps and noted that the behavior is not ideal. They announced a fix available via pull request #45528.

When a channel or DM has more unread messages than the first page loads, and someone posts while you’re scrolling through that history, every message between the end of the loaded page and the new live message is skipped. The list jumps from the older messages straight to the live one (often hours later), and nothing indicates that anything is missing. Reloading the channel fills the gap back in one page at a time, so a large gap takes several reloads.

This occurs independent of browser or device type (tested on Firefox Desktop and Android and Chrome Android PWA).

Steps to reproduce:

  1. User A and user B share a DM or channel. User A reads it up to the latest message, then leaves.
  2. User B posts 60+ messages.
  3. User A opens the DM. It loads around their last-read message (25 newer messages below).
  4. Before scrolling to the bottom of that loaded page, user B posts one more message.
  5. User A scrolls down.

Expected: the next 25-message page loads below, and so on until the live message.

Actual: the live message appears directly after the loaded page. Reaching the bottom requests messages after the live message, the response is empty, can_load_more_future becomes false, and the 35+ messages between are never shown. Because the live message is now visible, last_read_message_id advances past the gap, so the unread indicator clears as well.

Here is my best attempt at figuring out why this is happening:

Since a commit around December 2025 (“FEATURE: respect user presence when chat is on screen”, #36652), ChatChannel#onNewMessage always routes the message through ChatPaneState#handleIncomingMessage, which calls addMessage when auto-scrolling and via maintainScrollPosition otherwise.

ChatMessagesLoader#computeNextTargetMessage uses the last non-staged message in the list as the cursor for a FUTURE page. Once a live message sits at the end of a partially loaded list, that cursor jumps past all the unloaded history.

Then, Chat::MessagesQuery.query_paginated_messages only sets can_load_more_future for FUTURE pages and can_load_more_past for PAST pages. The other is nil. ChatMessagesLoader#loadMore assigns both from result.meta unconditionally, so a single PAST page (scrolling up a little after opening at last-read) sets canLoadMoreFuture to null while history below is still unloaded. Further pagination still works because loadMore checks === false.

On top of that, there is a separate, older bug where #sendNewMessage stages the message at the end of the list without loading the latest page when canLoadMoreFuture is true. Once confirmed, it becomes the FUTURE cursor, so the same skip happens if you reply to older messages in the history you’re reading.

Our community is not primarily chat-focused, but our users do back read frequently, so missing messages in an ongoing conversation you weren’t present for is a problem. I imagine it’ll be a bigger problems for communities that are primary-chat, especially given the new ability to set the homepage as chat.

3 Likes

Thanks for the report! I could repro using your steps and that certainly isn’t ideal.

I’ve added a fix here:

This will be available with Discourse updates soon.

4 Likes