Describe the bug
We've identified what appears to be a memory leak in livekit-client where Room instances remain strongly reachable after disconnect() and are never garbage collected.
The issue is reproducible not only in our production application, but also on the official LiveKit demo.
This suggests the problem is inside the SDK rather than in application code.
Reproduction
Steps to reproduce
- Open https://meet.livekit.io/.
- Join any room.
- Leave the room.
- Open Chrome DevTools → Memory.
- Click Collect garbage.
- Take a Heap Snapshot.
- Repeat the process multiple times.
Logs
System Info
livekit-client 2.19.2
Chrome (latest stable)
macOS
Reproduced both in:
our production React application
https://meet.livekit.io
Severity
Severity: High.
Every completed call permanently retains Room instances. The retained object graph grows linearly with the number of completed meetings and is expected to have an increasing memory impact over the lifetime of the page.
Additional Information
Actual result
Room instances remain in the heap after every completed call.
In my test I performed 6 completely independent join/leave cycles.
After each call I:
- left the room,
- forced garbage collection,
- created a new Heap Snapshot.
After the sixth call the heap contained: 12 retained Room instances (exactly 2 additional Room instances per completed call)
Expected result
After await room.disconnect(); and a garbage collection cycle, the Room instance should no longer be reachable.
A completed meeting should not permanently increase the number of retained Room objects.
Impact
The reproduction above was performed using a meeting with a single participant.
Even in this minimal scenario, completed meetings permanently accumulate retained Room instances.
Since a Room owns or references the entire session graph (participants, track publications, media tracks, peer connections, transports, etc.), the impact is expected to grow with meeting complexity.
For larger meetings (for example, 10+ participants), each retained Room is likely to keep references to substantially more objects than in the single-participant case, increasing the amount of memory that cannot be reclaimed after a meeting ends.
Why this is important
Room is not a lightweight object. It is the root of the LiveKit session graph and owns or references objects such as:
- LocalParticipant
- RemoteParticipants
- TrackPublications
- media tracks
- RTCEngine
- PCTransportManager
- RTCPeerConnections
- EventEmitters
- media transports
Retaining Room therefore means retaining a significant portion of the LiveKit session graph.
Over repeated join/leave cycles these retained session graphs accumulate, leading to steadily increasing memory usage during the lifetime of the page.
Suspected root cause
While investigating the SDK we noticed what may be the cause. In the Room constructor a devicechange listener is registered using an internal wrapper function.
Later, during disconnect, a different function reference appears to be passed to removeEventListener().
If this analysis is correct, the listener is never removed and continues to retain the associated closure.
We are not claiming this is definitively the root cause, only that it matches the observed heap-retention behavior.
Evidence
The issue reproduces:
✅ in our production application
✅ in the official LiveKit demo (meet.livekit.io)
✅ after explicit disconnect()
✅ after forced garbage collection
✅ across multiple independent join/leave cycles
The number of retained Room instances increases monotonically and never decreases.
Describe the bug
We've identified what appears to be a memory leak in livekit-client where Room instances remain strongly reachable after disconnect() and are never garbage collected.
The issue is reproducible not only in our production application, but also on the official LiveKit demo.
This suggests the problem is inside the SDK rather than in application code.
Reproduction
Steps to reproduce
Logs
System Info
Severity
Severity: High.
Every completed call permanently retains Room instances. The retained object graph grows linearly with the number of completed meetings and is expected to have an increasing memory impact over the lifetime of the page.
Additional Information
Actual result
Room instances remain in the heap after every completed call.
In my test I performed 6 completely independent join/leave cycles.
After each call I:
After the sixth call the heap contained: 12 retained Room instances (exactly 2 additional Room instances per completed call)
Expected result
After await room.disconnect(); and a garbage collection cycle, the Room instance should no longer be reachable.
A completed meeting should not permanently increase the number of retained Room objects.
Impact
The reproduction above was performed using a meeting with a single participant.
Even in this minimal scenario, completed meetings permanently accumulate retained Room instances.
Since a Room owns or references the entire session graph (participants, track publications, media tracks, peer connections, transports, etc.), the impact is expected to grow with meeting complexity.
For larger meetings (for example, 10+ participants), each retained Room is likely to keep references to substantially more objects than in the single-participant case, increasing the amount of memory that cannot be reclaimed after a meeting ends.
Why this is important
Room is not a lightweight object. It is the root of the LiveKit session graph and owns or references objects such as:
Retaining
Roomtherefore means retaining a significant portion of the LiveKit session graph.Over repeated join/leave cycles these retained session graphs accumulate, leading to steadily increasing memory usage during the lifetime of the page.
Suspected root cause
While investigating the SDK we noticed what may be the cause. In the Room constructor a devicechange listener is registered using an internal wrapper function.
Later, during disconnect, a different function reference appears to be passed to removeEventListener().
If this analysis is correct, the listener is never removed and continues to retain the associated closure.
We are not claiming this is definitively the root cause, only that it matches the observed heap-retention behavior.
Evidence
The issue reproduces:
✅ in our production application
✅ in the official LiveKit demo (meet.livekit.io)
✅ after explicit disconnect()
✅ after forced garbage collection
✅ across multiple independent join/leave cycles
The number of retained Room instances increases monotonically and never decreases.