Skip to content

fix: prevent _cancel_speech_pause from poisoning subsequent user turns - #5101

Merged
longcw merged 1 commit into
livekit:mainfrom
giulio-leone:fix/cancel-speech-pause-no-generation
Mar 13, 2026
Merged

fix: prevent _cancel_speech_pause from poisoning subsequent user turns#5101
longcw merged 1 commit into
livekit:mainfrom
giulio-leone:fix/cancel-speech-pause-no-generation

Conversation

@giulio-leone

Copy link
Copy Markdown
Contributor

Summary

Fixes a race condition where _cancel_speech_pause can permanently break user-turn processing, causing the assistant to silently stop replying.

Problem

When a user interrupts paused speech that has no active generation (e.g., the generation was never authorized or was cancelled before starting), _wait_for_generation() raises:

RuntimeError: cannot use wait_for_generation: no active generation is running.

This error bubbles up from _cancel_speech_pause and poisons the _cancel_speech_pause_task. Since subsequent tasks chain on the prior task via await old_task, the RuntimeError re-raises in every subsequent call, permanently breaking user-turn completion.

Impact: User speech is transcribed (STT events arrive) but the turn is never committed to chat context and no assistant reply is generated — for the current turn and all future turns.

Root Cause

In _cancel_speech_pause:

  1. await old_task — re-raises any error from a previously failed task
  2. await self._paused_speech._wait_for_generation() — raises RuntimeError if self._generations is empty

A paused speech handle with an empty _generations list triggers (2), which then triggers (1) on every subsequent call.

Fix

Two changes to _cancel_speech_pause:

  1. Guard _wait_for_generation(): Only call it when self._paused_speech._generations is non-empty. This prevents the RuntimeError from being raised in the first place.

  2. Catch errors from await old_task: Wrap it in try/except so a single failed task doesn't permanently poison the task chain. The error is logged at debug level and subsequent turns can proceed normally.

Testing

The fix is defensive — it prevents an uncaught exception from a known race condition. The scenario requires specific timing between STT events, pause/resume, and generation authorization that is difficult to unit-test deterministically. The fix follows the same defensive pattern used in _mark_done() (which uses contextlib.suppress for similar edge cases).

Closes #5100

Guard _wait_for_generation() call — only invoke it when the paused speech
actually has active generations (self._paused_speech._generations is
non-empty).  A paused speech that was never authorized or whose generation
was cancelled before starting will have an empty _generations list, and
calling _wait_for_generation() on it raises RuntimeError.

Additionally, wrap 'await old_task' in try/except so a failed prior
_cancel_speech_pause task does not keep re-raising through subsequent
tasks that chain on it.  Without this, one race-condition failure
permanently poisons the task chain and all subsequent user turns silently
fail (no assistant reply generated).

Closes #5100

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@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.


giulio-leone seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
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: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

@longcw
longcw merged commit f3cbd00 into livekit:main Mar 13, 2026
10 of 11 checks passed
iancarrasco-b10 pushed a commit to iancarrasco-b10/agents that referenced this pull request Mar 17, 2026
livekit#5101)

Co-authored-by: giulio-leone <giulio.leone@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
russellmartin-livekit pushed a commit that referenced this pull request Apr 13, 2026
#5101)

Co-authored-by: giulio-leone <giulio.leone@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Bug: _user_turn_completed_task can be poisoned by _cancel_speech_pause when paused speech has no active generation

3 participants