From 16409c91114e92715003f2846870728195c84785 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 31 Mar 2026 15:53:35 +0100 Subject: [PATCH 1/2] Allow fully read markers to "go backwards" Allow `m.fully_read` markers to be updated to older events. Previously this was blocked if the update referred to an event that was older in terms of topological, then stream as a tie-breaker. The spec does not forbid this behaviour, and a client developer would like to be able to do so to provide "undo" functionality for marking events as read. --- synapse/handlers/read_marker.py | 38 ++++++++++++++------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/synapse/handlers/read_marker.py b/synapse/handlers/read_marker.py index 85d2dd62bb7..ab98c39f76f 100644 --- a/synapse/handlers/read_marker.py +++ b/synapse/handlers/read_marker.py @@ -23,7 +23,6 @@ from typing import TYPE_CHECKING from synapse.api.constants import ReceiptTypes -from synapse.api.errors import SynapseError from synapse.util.async_helpers import Linearizer if TYPE_CHECKING: @@ -55,25 +54,20 @@ async def received_client_read_marker( user_id, room_id, ReceiptTypes.FULLY_READ ) - should_update = True - # Get event ordering, this also ensures we know about the event - event_ordering = await self.store.get_event_ordering(event_id, room_id) + # Ignore subsequent attempts to apply the same read marker. + if ( + existing_read_marker is not None + and not isinstance(existing_read_marker.get("event_id"), str) + and event_id == existing_read_marker.get("event_id") + ): + return - if existing_read_marker: - try: - old_event_ordering = await self.store.get_event_ordering( - existing_read_marker["event_id"], room_id - ) - except SynapseError: - # Old event no longer exists, assume new is ahead. This may - # happen if the old event was removed due to retention. - pass - else: - # Only update if the new marker is ahead in the stream - should_update = event_ordering > old_event_ordering - - if should_update: - content = {"event_id": event_id} - await self.account_data_handler.add_account_data_to_room( - user_id, room_id, ReceiptTypes.FULLY_READ, content - ) + # Update the user's account data with the new read marker. + # + # Note: it's OK if this update refers to an *older* event than the + # previous read marker; a user may have accidentally marked some + # messages as read, and want to undo it. + content = {"event_id": event_id} + await self.account_data_handler.add_account_data_to_room( + user_id, room_id, ReceiptTypes.FULLY_READ, content + ) From 5c2ec682f6b1981c4dd7f8c5ac84f46dc55650e7 Mon Sep 17 00:00:00 2001 From: Andrew Morgan Date: Tue, 31 Mar 2026 15:58:22 +0100 Subject: [PATCH 2/2] newsfile --- changelog.d/19635.misc | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/19635.misc diff --git a/changelog.d/19635.misc b/changelog.d/19635.misc new file mode 100644 index 00000000000..5fcd8499c5b --- /dev/null +++ b/changelog.d/19635.misc @@ -0,0 +1 @@ +Allow clients to set their user's `m.fully_read` marker back to older events. \ No newline at end of file