Skip to content

fix: keep attemptConnection on its own engine when disconnect races the join - #2151

Merged
lukasIO merged 3 commits into
mainfrom
claude/livekit-issue-2150-ce107d
Oct 19, 2026
Merged

lukasIO merged 3 commits into
mainfrom
claude/livekit-issue-2150-ce107d

Conversation

@lukasIO

@lukasIO lukasIO commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

closes #2150

Room.attemptConnection reads this.engine again after it awaits the signal join. A concurrent room.disconnect() sets this.engine to undefined during that await. The cleanup path then calls this.engine.close() and throws a TypeError. The reporter of #2150 saw these errors when they left a room about 1 s after connect().

This change captures the engine once, before the first await, and uses that reference for the whole attempt. The cleanup paths close that engine. They call recreateEngine() only if it is still the room's engine.

A newer connection is the failure case. If connect() runs again after disconnect() while the old join is still pending, the room holds a new engine. When the old join fails, its cleanup closes this.engine, which is the new engine, and so it breaks the new connection. With this change, the stale attempt closes only its own engine, and it does not disconnect the room.

Impact

Two tests in Room.test.ts hold the join open, call disconnect(), and then fail the join. On main, the first test gets TypeError: Cannot read properties of undefined instead of a cancelled ConnectionError. The second test shows that the stale attempt closes the newer engine. Both tests pass with this change. The rate of this race in production is not measured.

The orphaned-engine rejoin in #2150 comes from RTCEngine.join() reopening a closed engine. #2095 fixes that part on main, and it is not released yet. This PR covers the remaining Room side of the issue.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a840412

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

This PR includes changesets to release 1 package
Name Type
livekit-client 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

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size
dist/livekit-client.esm.mjs 115.77 KB (-0.07% 🔽)
dist/livekit-client.umd.js 125.06 KB (-0.02% 🔽)
dist/core/core.mjs 78.55 KB (-0.18% 🔽)

devin-ai-integration[bot]

This comment was marked as resolved.

Comment thread src/room/Room.ts Outdated
lukasIO and others added 3 commits October 19, 2026 12:12
…he join

Room.attemptConnection re-read this.engine after awaiting the signal
join. A concurrent disconnect() clears this.engine, so the cleanup path
threw a TypeError. A newer connect() replaces it, so a stale attempt
closed the newer engine. Capture the engine once and only recreate it if
it is still the room's engine.

Refs #2150

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A connect() attempt that fails after disconnect() and a newer connect()
still called handleDisconnect on the shared Room. That moved the newer
attempt to Disconnected and emitted RoomEvent.Disconnected. Only the
attempt that owns the room's abort controller now touches shared state,
and region failover checks the attempt's own abort signal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lukasIO
lukasIO force-pushed the claude/livekit-issue-2150-ce107d branch from e0490a3 to a840412 Compare October 19, 2026 10:13
@lukasIO
lukasIO merged commit e59279a into main Oct 19, 2026
5 checks passed
@lukasIO
lukasIO deleted the claude/livekit-issue-2150-ce107d branch October 19, 2026 10:23
@github-actions github-actions Bot mentioned this pull request Oct 19, 2026
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.

RTCEngine.join() reopens an engine closed mid-join, so Room.disconnect() during connect/reconnect leaves an orphaned engine that keeps rejoining

2 participants