You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Events emitted while engine.pendingReconnect is still true after Reconnected stay in bufferedEvents until the next reconnect #2183
After a full reconnect (restartConnection), the Room can become connected and emit RoomEvent.Reconnected while engine.pendingReconnect is still true. Events emitted through emitWhenConnected in that window are pushed to room.bufferedEvents, but the buffer is only flushed on the nextReconnected/Resumed. If no further reconnect happens, the events are never delivered (and a later reconnect clears the buffer).
Sequence we observe:
restartConnection arms engine.reconnectTimeout and clears it only in the .finally of the whole attempt, so engine.pendingReconnect stays true until the attempt fully settles.
Room.handleSignalRestarted awaits engine.waitForRestarted(), which resolves immediately if pcState is already Connected. The room sets state connected, flushes bufferedEvents and emits Reconnectedbefore the engine attempt has finished.
In the window "room connected, engine.pendingReconnect === true" (1.2–1.8 s in our measurements), emitWhenConnected takes the buffering branch:
Nothing flushes the buffer afterwards. ParticipantConnected, TrackPublished, TrackSubscribed for a remote participant who (re)joins in that window are lost to the application: useTracks-style UIs show the participant without video, or not at all.
Reproduction
Two participants A and B in a room.
Force a full reconnect on A (e.g. room.engine.client.close()/signal drop leading to restartConnection, not a resume).
While A is reconnecting, have B leave and rejoin (or publish a new camera track) so that B's events reach A within ~1–2 s after A's Reconnected.
On A: room.bufferedEvents.length > 0 stays non-zero indefinitely; no TrackSubscribed for B's new track is emitted, although room.remoteParticipants.get(B).trackPublications contains the subscribed track.
Logs
System Info
**livekit-client version:** 2.22.1 (code path unchanged in 2.23.0)
**Browser:** Chrome 13x (headless and desktop), reproducible deterministically on a local stand
Severity
annoyance
Additional Information
Expected behavior
Either Reconnected is emitted only after the engine attempt has fully settled (pendingReconnect === false), or the buffer is flushed when pendingReconnect becomes false while the room is connected.
Workaround
We poll every 500 ms: when room.state === 'connected' && !room.isResuming && !engine.pendingReconnect && !engine.attemptingReconnect && room.bufferedEvents.length > 0, we re-emit the buffered events ourselves (dropping events for participants/publications/tracks that no longer exist) and clear the buffer. With this workaround the lost-tile symptom disappears in our end-to-end test (rejoin-peer-video).
One acceptance criterion I would add is that Reconnected should not be treated as a recovery commit point until the engine attempt has settled. After Reconnected, wait for pendingReconnect === false, then require both the expected TrackSubscribed event and an advancing framesDecoded counter for that publication. Without the second check, fixing the event buffer can still leave a connected-but-frozen tile.
A compact regression could have participant B leave and republish 200–600 ms after A receives Reconnected. Fail if the event stays buffered, fires more than once on the next resume, or the remote frame counter does not advance within the agreed deadline.
Commercial disclosure: I maintain a WebRTC source kit and work on recovery testing. This comment is about the recovery success predicate, not about changing stacks.
Activity
AiLaoHuYu commentedon Oct 26, 2026
One acceptance criterion I would add is that
Reconnectedshould not be treated as a recovery commit point until the engine attempt has settled. AfterReconnected, wait forpendingReconnect === false, then require both the expectedTrackSubscribedevent and an advancingframesDecodedcounter for that publication. Without the second check, fixing the event buffer can still leave a connected-but-frozen tile.A compact regression could have participant B leave and republish 200–600 ms after A receives
Reconnected. Fail if the event stays buffered, fires more than once on the next resume, or the remote frame counter does not advance within the agreed deadline.Commercial disclosure: I maintain a WebRTC source kit and work on recovery testing. This comment is about the recovery success predicate, not about changing stacks.