Skip to content

fix(voice): cancel parked preemptive generation before pausing the activity - #6865

Open
uuzzrm wants to merge 1 commit into
livekit:mainfrom
uuzzrm:fix/pause-preemptive-deadlock
Open

fix(voice): cancel parked preemptive generation before pausing the activity#6865
uuzzrm wants to merge 1 commit into
livekit:mainfrom
uuzzrm:fix/pause-preemptive-deadlock

Conversation

@uuzzrm

@uuzzrm uuzzrm commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #6858

What changed

When the caller's turn ends while an uninterruptible message is playing,
_user_turn_completed_task discards the turn and returns early. If a
speculative reply had already been started (preemptive generation), it
stayed parked in _preemptive_generation - never scheduled, never
cancelled.

Awaiting an AgentTask takes the pause path, and pause() waits for all
outstanding speech work. With the parked reply still in _speech_tasks,
that wait never completes, so the handoff hangs until the session is
closed (or the caller hangs up).

drain(), aclose() and interrupt() all cancel the parked generation
before waiting; pause() was the only one that didn't.

Changes

  • _user_turn_completed_task: cancel the speculative reply when the turn
    is discarded, since it can never be scheduled.
  • pause(): cancel any parked preemptive generation before waiting for
    the speech tasks to drain, matching the other teardown paths.

Tests

  • New tests/test_preemptive_pause_deadlock.py with two cases:
    • the discard path drops the parked reply (previously left it behind);
    • pause() completes in the presence of a parked generation instead of
      hanging.
  • pytest tests/test_preemptive_pause_deadlock.py - 2 passed.
  • Related agent/voice suites pass; ruff check and ruff format --check
    are clean.

…tivity

A preemptive reply started while the user was talking can end up parked in
_preemptive_generation when the turn is discarded because an
uninterruptible message is playing. pause() waits for all outstanding
speech work, so an awaited AgentTask (whose handoff takes the pause path)
would hang until the session closed.

Drop the parked generation in both places:
- on the turn-discard path in _user_turn_completed_task, since the
  speculative reply can never be scheduled, and
- in pause(), matching drain()/aclose()/interrupt(), so a handoff can
  never wait on a generation nobody scheduled.

Adds a regression test covering both guards.

@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

@uuzzrm uuzzrm closed this Aug 15, 2026
@uuzzrm uuzzrm reopened this Aug 15, 2026
@uuzzrm

uuzzrm commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

Reopening this - the deadlock in #6858 is still reproducible on the latest agent (we hit it again today on a handoff where the caller spoke during an uninterruptible reply). The branch was synced with main and the focused test suite passes locally; re-running CI on the current head now.

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.

AgentTask handoff deadlocks when a preemptive generation is left parked

1 participant