Skip to content

Fix stream controller map leak on disconnect#651

Closed
LautaroPetaccio wants to merge 2 commits intolivekit:mainfrom
LautaroPetaccio:fix/stream-controller-map-leak
Closed

Fix stream controller map leak on disconnect#651
LautaroPetaccio wants to merge 2 commits intolivekit:mainfrom
LautaroPetaccio:fix/stream-controller-map-leak

Conversation

@LautaroPetaccio
Copy link
Copy Markdown
Contributor

Summary

When a sender's data stream is aborted (e.g., due to a transport failure during sendChunk), no DataStream_Trailer is sent to the remote side. On the receiver, handleStreamTrailer() is the only code path that calls controller.close() and removes entries from the byteStreamControllers / textStreamControllers Maps. Since room.disconnect() does not clear these Maps either, the StreamController objects (containing header, ReadableStreamDefaultController, and startTime) leak for the entire lifetime of the Room object.

Changes

Receiver side (room.ts):

  • Added cleanupStreamControllers() that iterates both Maps, errors any open controllers (so consumers get a proper error instead of hanging indefinitely), and clears the entries.
  • Called from disconnect() before removing event listeners.

Sender side (participant.ts):

  • Changed the abort() handlers in both streamText() and streamBytes() to send a DataStream_Trailer with the error reason on a best-effort basis. If the transport is already down, the send fails silently — the receiver-side cleanup handles that case.

When a sender's stream is aborted, no trailer is sent, so the receiver
never cleans up its byteStreamControllers/textStreamControllers Map
entries. These entries leak for the lifetime of the Room object.

Two complementary fixes:

1. Receiver side (room.ts): Add cleanupStreamControllers() called on
   disconnect() that errors any open controllers and clears both Maps.
   This ensures consumers get a proper error instead of hanging forever,
   and prevents stale entries from accumulating.

2. Sender side (participant.ts): In abort() handlers for streamText()
   and streamBytes(), attempt to send a DataStream_Trailer with the
   error reason. This is best-effort — if the transport is already down
   the send will fail silently, and the receiver-side cleanup handles
   that case.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 14, 2026

🦋 Changeset detected

Latest commit: c23e2b8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@livekit/rtc-node Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

@LautaroPetaccio
Copy link
Copy Markdown
Contributor Author

Closing: equivalent fix already merged upstream in #633

@LautaroPetaccio LautaroPetaccio deleted the fix/stream-controller-map-leak branch April 14, 2026 14:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant