Skip to content

fix(voice): capture activity to local before awaits in closeImplInner#1872

Open
tsushanth wants to merge 1 commit into
livekit:mainfrom
tsushanth:fix/close-impl-activity-race
Open

fix(voice): capture activity to local before awaits in closeImplInner#1872
tsushanth wants to merge 1 commit into
livekit:mainfrom
tsushanth:fix/close-impl-activity-race

Conversation

@tsushanth

Copy link
Copy Markdown
Contributor

Fixes #1871

closeImplInner reads this.activity across multiple await boundaries without first capturing a local reference. When close() races an activity transition that sets this.activity = undefined between the initial if (this.activity) guard and the subsequent awaited calls, accesses like this.activity.currentSpeech?.waitForPlayout() throw a TypeError:

TypeError: Cannot read properties of undefined (reading 'currentSpeech')
    at AgentSession.closeImplInner (voice/agent_session.js:968)

Because the throw happens before this.emit(Close, ...), the Close event is never emitted — so any application code that depends on the session close event silently never runs for those sessions.

The fix captures this.activity into a local const activity at the top of the block. All reads within the if (activity) branch then operate on the captured reference, which remains stable regardless of concurrent mutations to this.activity.

When close() races an activity transition that sets this.activity to
undefined between the if-guard and subsequent awaits, accesses like
this.activity.currentSpeech throw TypeError. Capturing the reference
to a local variable at the top of the block makes all reads within
the if-branch consistent regardless of concurrent mutations.

Fixes livekit#1871
@changeset-bot

changeset-bot Bot commented Jun 24, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 83eeaf5

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Captured activity local variable not used for close(), defeating the purpose of the race-condition fix

The PR captures this.activity into a local activity variable at line 1529 to protect against concurrent mutation during the multiple await points in closeImplInner. However, line 1564 still uses this.activity?.close() instead of activity?.close(). A concurrent _updateActivity call can set this.activity = undefined at agent_session.ts:1176 when it sees this.closing = true (set at line 1524). If that happens between lines 1529 and 1564, the original activity captured in the local variable will never have close() called on it — resulting in a resource leak — while this.activity?.close() becomes a no-op.

(Refers to line 1564)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

AgentSession.closeImplInner throws "Cannot read properties of undefined (reading 'currentSpeech')" when close races an activity transition

3 participants