mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 13:28:04 +00:00
Reflect out-of-band leaves in state_after on /sync
Out-of-band membership events are outliers. They never enter the current state delta stream, so `_generate_room_entry` returns an empty `state` for the archived room they belong to. MSC4222 says clients "MUST only update their local state using `state_after` and NOT consider the events that appear in the timeline". A client on `state_after` therefore never learns that the user rejected a remote invite or rescinded a knock, and Element Web keeps showing the knock as pending. This copies the user's out-of-band membership event into `state_after`. It reads `room_builder.events`, the unfiltered timeline, as sliding sync does in `get_room_sync_data`: a timeline filter that drops `m.room.member` still gets the leave. The event is already in memory, so sync makes no new query. #20081 and #20314 fixed `state_after` for a leave that empties the room of local users by recovering state in `_compute_state_delta_for_incremental_sync`. An out-of-band leave has no state group and never reaches that function, because `_generate_room_entry` skips state computation for out-of-band rooms. A timeline filter that empties the batch still gets no room at all: the empty-batch shortcut returns first. The FIXME in `tests/handlers/test_sync.py` records that limitation. Co-authored-by: Quentin Gliech <quenting@element.io> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
ca28da9663
commit
489e627b59
2 files changed
+20
No files matched your search
@@ -0,0 +1 @@
|
||||
Fix a bug where a client using `state_after` ([MSC4222](https://github.com/matrix-org/matrix-spec-proposals/pull/4222)) would keep showing a room as invited or knocked after the user rejected the invite or rescinded the knock.
|
||||
@@ -3570,6 +3570,25 @@ class SyncHandler:
|
||||
# An out of band room won't have any state changes.
|
||||
state = {}
|
||||
|
||||
# MSC4222 clients do not apply timeline events to room state, so
|
||||
# the out-of-band membership has to appear in `state_after` or they
|
||||
# never learn that the user rejected the invite or rescinded the
|
||||
# knock. Sliding sync does the same in `get_room_sync_data`.
|
||||
#
|
||||
# `events` is unfiltered, so a client filter that hides the
|
||||
# membership event still gets it in `state_after`. A filter that
|
||||
# empties the timeline batch gets no room at all, as the shortcut
|
||||
# above returns first (see the FIXME in
|
||||
# `tests/handlers/test_sync.py`).
|
||||
if sync_config.use_state_after:
|
||||
for oob_event in events or ():
|
||||
if (
|
||||
oob_event.type == EventTypes.Member
|
||||
and oob_event.state_key == sync_config.user.to_string()
|
||||
and oob_event.internal_metadata.is_out_of_band_membership()
|
||||
):
|
||||
state[(oob_event.type, oob_event.state_key)] = oob_event
|
||||
|
||||
summary: JsonDict | None = {}
|
||||
|
||||
# we include a summary in room responses when we're lazy loading
|
||||
|
||||
Reference in new issue
Block a user