When using the Matrix Rust SDK through FFI on iOS, my team hit an event-cache/SQLite inconsistency after this flow:
- Receive a room invite
- Decline the invite
- Receive a new invite for the same room
- Accept the invite
- Open the timeline or paginate
After accepting the second invite, the room can fail to load correctly, and timeline pagination fails with SQLite/event-cache errors. Interestingly, the issue does not happen if we avoid forgetting the room after declining the invitation by modifying Room::leave_impl in matrix-sdk.
Relevant logs:
2026-06-25T22:15:01.945836Z DEBUG matrix_sdk_sqlite::event_cache_store: Timer _method_ finished in 68.666µs | crates/matrix-sdk-sqlite/src/event_cache_store.rs:1295 | spans: room_updates_task > handle_room_updates > handle_joined_room_update{room_id=!foCbxZKjITZLwSsNci:...} > filter_duplicated_events{linked_chunk_id=Room("!foCbxZKjITZLwSsNci:...")}
2026-06-25T22:15:01.945977Z TRACE matrix_sdk_sqlite::event_cache_store: pushing 10 items @ 2 linked_chunk_id=!foCbxZKjITZLwSsNci:... | crates/matrix-sdk-sqlite/src/event_cache_store.rs:740
2026-06-25T22:15:01.946052Z DEBUG matrix_sdk_sqlite::event_cache_store: Timer _method_ finished in 138.334µs | crates/matrix-sdk-sqlite/src/event_cache_store.rs:640 | spans: handle_linked_chunk_updates{linked_chunk_id=Room("!foCbxZKjITZLwSsNci:...")}
2026-06-25T22:15:01.946072Z ERROR matrix_sdk::event_cache: handling joined room update: FOREIGN KEY constraint failed room_id=!foCbxZKjITZLwSsNci:... | crates/matrix-sdk/src/event_cache/mod.rs:690 | spans: room_updates_task > handle_room_updates
2026-06-25T22:15:01.996279Z TRACE matrix_sdk_base::response_processors::changes: ready to submit changes to store | crates/matrix-sdk-base/src/response_processors/changes.rs:58 | spans: fetch_members > receive_all_members{room_id="!foCbxZKjITZLwSsNci:..."} > save_and_apply
2026-06-25T22:15:01.996642Z TRACE matrix_sdk_base::response_processors::changes: applied changes | crates/matrix-sdk-base/src/response_processors/changes.rs:67 | spans: fetch_members > receive_all_members{room_id="!foCbxZKjITZLwSsNci:..."} > save_and_apply
2026-06-25T22:15:01.996646Z TRACE matrix_sdk_base::response_processors::changes: Timer __method_ finished in 367.625µs | crates/matrix-sdk-base/src/response_processors/changes.rs:56 | spans: fetch_members > receive_all_members{room_id="!foCbxZKjITZLwSsNci:..."} > save_and_apply
2026-06-25T22:15:01.996718Z TRACE matrix_sdk_ui::timeline::controller: Updating missing sender profiles | crates/matrix-sdk-ui/src/timeline/controller/mod.rs:1023 | spans: fetch_members
2026-06-25T22:15:01.996965Z TRACE matrix_sdk_ui::timeline::controller: Adding profile event_id="$AINJMKSmGQio0S0ioGHa7m32zsFCQHHzFPoOdcKWgb8" | crates/matrix-sdk-ui/src/timeline/controller/mod.rs:1039 | spans: fetch_members
2026-06-25T22:15:01.996975Z TRACE matrix_sdk_ui::timeline::controller: Done updating missing sender profiles | crates/matrix-sdk-ui/src/timeline/controller/mod.rs:1059 | spans: fetch_members
[ROOM] Timeline updated [MatrixSDKFFI.TimelineDiff.reset(values: [MatrixSDKFFI.TimelineItem, MatrixSDKFFI.TimelineItem])]
[ROOM] Timeline listener added !foCbxZKjITZLwSsNci:...
2026-06-25T22:15:02.740265Z DEBUG matrix_sdk_sqlite::event_cache_store: Timer _method_ finished in 250ns | crates/matrix-sdk-sqlite/src/event_cache_store.rs:1295 | spans: paginate_backwards{room_id="!foCbxZKjITZLwSsNci:..."} > run_backwards_once{batch_size=30} > filter_duplicated_events{linked_chunk_id=Room("!foCbxZKjITZLwSsNci:...")}
2026-06-25T22:15:02.740496Z TRACE matrix_sdk_sqlite::event_cache_store: removing chunk @ 1 linked_chunk_id=!foCbxZKjITZLwSsNci:... | crates/matrix-sdk-sqlite/src/event_cache_store.rs:708
2026-06-25T22:15:02.740581Z DEBUG matrix_sdk_sqlite::event_cache_store: Timer _method_ finished in 169.834µs | crates/matrix-sdk-sqlite/src/event_cache_store.rs:640 | spans: handle_linked_chunk_updates{linked_chunk_id=Room("!foCbxZKjITZLwSsNci:...")}
2026-06-25T22:15:02.740776Z WARN matrix_sdk::event_cache::caches::pagination: event cache back-pagination failed: Query returned no rows | crates/matrix-sdk/src/event_cache/caches/pagination.rs:179
ERROR: MatrixSDKFFI.ClientError.Generic(msg: "Query returned no rows", details: Optional("EventCacheError(Storage(Backend(Sqlite(QueryReturnedNoRows))))"))
This suggests that the room may be removed from persistent storage when it is forgotten, while some SDK-side room/event-cache state, such as latest-event handling, room update handling, or timeline pagination state, may still reference the old room lifecycle and is not fully invalidated or rebuilt when the same room is invited and joined again.
We were able to bypass the issue by modifying Room::leave_impl so the room is not forgotten when declining an invitation, but this is only a workaround and not a definitive fix.
When using the Matrix Rust SDK through FFI on iOS, my team hit an event-cache/SQLite inconsistency after this flow:
After accepting the second invite, the room can fail to load correctly, and timeline pagination fails with SQLite/event-cache errors. Interestingly, the issue does not happen if we avoid forgetting the room after declining the invitation by modifying
Room::leave_implinmatrix-sdk.Relevant logs:
This suggests that the room may be removed from persistent storage when it is forgotten, while some SDK-side room/event-cache state, such as latest-event handling, room update handling, or timeline pagination state, may still reference the old room lifecycle and is not fully invalidated or rebuilt when the same room is invited and joined again.
We were able to bypass the issue by modifying
Room::leave_implso the room is not forgotten when declining an invitation, but this is only a workaround and not a definitive fix.