mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-05 00:17:57 +00:00
filter_and_transform_events_for_client annotates each event with the requesting user's membership (MSC4115, `unsigned.membership`). It reads that from the state after the event, and it fetches state only for events whose in-memory copy is not flagged as an outlier. An event let through by `always_include_ids` has no state to look in when our copy still carries the outlier flag, and the "unreachable!" branch raised, failing the whole `/sync` with a 500. Two reads inside one `/sync` disagree. `_load_filtered_recents` builds `always_include_ids` from an uncached read of `current_state_events`, which sees an out-of-band invite or rejection the moment the persister de-outliers it into the current state. The event object comes from the event cache and keeps `outlier=True` until the invalidation reaches that process. No ordering of commit and invalidation closes the gap. MSC4115 makes `unsigned.membership` optional and lets a server omit it when computing it is impossible, so return the event unannotated and log the event and room at info. `FilteredEvent.membership` documents this third case where it is None. The call-invite assert stays. Synapse only flags state events as outliers (out-of-band memberships, auth events, MSC4242 state DAG events), so a non-state call invite always has state. Its comment now gives that reason instead of claiming earlier code filters outliers. The regression test filters a federated invite and its local rejection for a third user with the invite in `always_include_ids`. On develop it fails with the 500's exception. The invite setup moves into a helper shared with test_out_of_band_invite_rejection. Fixes #19858. Co-authored-by: Quentin Gliech <quenting@element.io> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>