Problem
getServerSetInRoom (in StateService) only includes servers that have at least one user with membership: 'join'. When a user is banned, their membership changes from join to ban. If they were the only user from their server, that server is excluded from the destination set for subsequent events — including the unban event.
What happens today
- Server A bans a user from Server B
- Ban event is sent to Server B (user still had
join when server set was computed)
- Server B processes the ban, membership updates to
ban
- Server A unbans the user (sends
membership: leave via kickUser)
sendEventToAllServersInRoom computes the server set — Server B has no join members → excluded from destinations
- The leave (unban) event is never delivered to Server B
- Server A tries to re-invite the user — builds an invite whose
prev_events reference the leave event
- Server A sends the invite to Server B via
/v2/invite
- Server B has
m.room.create (user had previously joined), so processInvite calls handlePdu
handlePdu → _resolveStateAtEvent → looks for stateId of prev_events → the leave event was never received → "no previous state for event" error
- Invite processing fails, Server A gets a 500
Impact
- After a ban+unban cycle in a federated room, the user cannot be re-invited
- The state chain on the remote server becomes broken because intermediate events are missing
- Affects RC ↔ RC federation. RC ↔ Element (Synapse) works because Synapse handles server routing differently
Root cause
The Matrix spec states that servers should continue receiving events for rooms where they have any membership state (join, invite, ban, leave with prior membership). Our getServerSetInRoom only considers join membership, which is too restrictive.
Proposed fix
getServerSetInRoom should include servers that have users with ban or invite membership in addition to join. A banned user's server still needs to receive room events (at minimum the unban event) to maintain a consistent state chain.
From the spec perspective, the set of servers that should receive events ("resident servers") includes any server with at least one user with membership join or invite. For ban, the server should receive at minimum the events needed to transition out of the banned state.
Simpler alternative: when sending a ban-related event (membership: leave after ban), explicitly add the target user's server to the destination set regardless of their current membership.
Problem
getServerSetInRoom(inStateService) only includes servers that have at least one user withmembership: 'join'. When a user is banned, their membership changes fromjointoban. If they were the only user from their server, that server is excluded from the destination set for subsequent events — including the unban event.What happens today
joinwhen server set was computed)banmembership: leaveviakickUser)sendEventToAllServersInRoomcomputes the server set — Server B has nojoinmembers → excluded from destinationsprev_eventsreference the leave event/v2/invitem.room.create(user had previously joined), soprocessInvitecallshandlePduhandlePdu→_resolveStateAtEvent→ looks forstateIdofprev_events→ the leave event was never received → "no previous state for event" errorImpact
Root cause
The Matrix spec states that servers should continue receiving events for rooms where they have any membership state (join, invite, ban, leave with prior membership). Our
getServerSetInRoomonly considersjoinmembership, which is too restrictive.Proposed fix
getServerSetInRoomshould include servers that have users withbanorinvitemembership in addition tojoin. A banned user's server still needs to receive room events (at minimum the unban event) to maintain a consistent state chain.From the spec perspective, the set of servers that should receive events ("resident servers") includes any server with at least one user with membership
joinorinvite. Forban, the server should receive at minimum the events needed to transition out of the banned state.Simpler alternative: when sending a ban-related event (
membership: leaveafter ban), explicitly add the target user's server to the destination set regardless of their current membership.