Files
synapse/tests
f2c8d2fff6 Don't 500 /sync when an always-included event has no state
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>
2026-10-30 10:20:37 +01:00
..
2026-09-18 12:00:45 +01:00