Summary
Room.roomInfo() returns pinnedEventIds: [] for rooms that have a valid server-side m.room.pinned_events state event with pinned events in it. This is observable even in the initial baseline snapshot right after the room is opened — not a "state not loaded yet" race.
Environment
- Package:
@unomed/react-native-matrix-sdk 0.9.1
- Runtime: React Native 0.7x (Expo dev client), Android emulator
- Homeserver: Synapse 1.156.0 behind Matrix Authentication Service (MAS)
- Room:
!QqqEqsAgTkbdletaNs:muni.rappler.com with m.room.pinned_events containing 3 event IDs at the time of testing
Repro
- Join a room that has pinned messages (verify via
GET /_matrix/client/v3/rooms/{roomId}/state/m.room.pinned_events — returns {"pinned": ["$evt1", "$evt2", ...]}).
- In the app, read
Room.roomInfo().pinnedEventIds immediately after room open (baseline snapshot) and after subsequent room-info updates.
- Observed:
pinnedEventIds is [] in every snapshot, while the server state endpoint returns the 3 pinned IDs.
Related observation: Timeline.pinEvent(eventId) can return false when the SDK's stale local state believes the event is already pinned — which makes pin/unpin actions look like silent no-ops.
Expected
RoomInfo.pinnedEventIds should match the server-side m.room.pinned_events content.
Useful context for triage
The timeline diff surface DOES deliver m.room.pinned_events changes correctly: a pin made by another member produces a TimelineItemContent.State item with inner.content.tag === 'RoomPinnedEvents' in the Timeline.addListener diff within seconds (verified live). So the state-event pipeline works; the RoomInfo cache path appears to be the broken piece.
Workaround (current)
Because the field is unreliable, we read the authoritative list directly from the homeserver state endpoint and refresh when a RoomPinnedEvents state item arrives in the timeline diff, with a periodic poll as a backstop. A fix to RoomInfo.pinnedEventIds (or an explicit "this field is not populated" documentation) would let clients drop the workaround.
Summary
Room.roomInfo()returnspinnedEventIds: []for rooms that have a valid server-sidem.room.pinned_eventsstate event with pinned events in it. This is observable even in the initial baseline snapshot right after the room is opened — not a "state not loaded yet" race.Environment
@unomed/react-native-matrix-sdk0.9.1!QqqEqsAgTkbdletaNs:muni.rappler.comwithm.room.pinned_eventscontaining 3 event IDs at the time of testingRepro
GET /_matrix/client/v3/rooms/{roomId}/state/m.room.pinned_events— returns{"pinned": ["$evt1", "$evt2", ...]}).Room.roomInfo().pinnedEventIdsimmediately after room open (baseline snapshot) and after subsequent room-info updates.pinnedEventIdsis[]in every snapshot, while the server state endpoint returns the 3 pinned IDs.Related observation:
Timeline.pinEvent(eventId)can returnfalsewhen the SDK's stale local state believes the event is already pinned — which makes pin/unpin actions look like silent no-ops.Expected
RoomInfo.pinnedEventIdsshould match the server-sidem.room.pinned_eventscontent.Useful context for triage
The timeline diff surface DOES deliver
m.room.pinned_eventschanges correctly: a pin made by another member produces aTimelineItemContent.Stateitem withinner.content.tag === 'RoomPinnedEvents'in theTimeline.addListenerdiff within seconds (verified live). So the state-event pipeline works; theRoomInfocache path appears to be the broken piece.Workaround (current)
Because the field is unreliable, we read the authoritative list directly from the homeserver state endpoint and refresh when a
RoomPinnedEventsstate item arrives in the timeline diff, with a periodic poll as a backstop. A fix toRoomInfo.pinnedEventIds(or an explicit "this field is not populated" documentation) would let clients drop the workaround.