Skip to content

fix: resolve voice pipeline deadlock on TTS error and retrieve background task exceptions#3921

Open
AAliKKhan wants to merge 4 commits into
openai:mainfrom
AAliKKhan:main
Open

fix: resolve voice pipeline deadlock on TTS error and retrieve background task exceptions#3921
AAliKKhan wants to merge 4 commits into
openai:mainfrom
AAliKKhan:main

Conversation

@AAliKKhan

Copy link
Copy Markdown
Contributor

This pull request fixes three issues in the voice pipeline error handling:

  1. Deadlock when TTS streaming fails — When _stream_audio\ encounters an exception, the error handler now puts the error on the public queue (via _add_error) and cancels the producer task instead of putting \session_ended\ on the local queue. Previously, \stream()\ would see the \session_ended\ event, enter the \�syncio.shield\ path, and hang indefinitely waiting for a producer that hadn't finished.

  2. Background task exception never retrieved — _check_errors\ only inspected _stream_audio\ tasks, missing \ ext_generation_task. Added it to the check with a \ ask.cancelled()\ guard to prevent \CancelledError\ from being raised.

  3. No \None\ sentinel on error — When a TTS task fails, the dispatcher's inner loop needed a \None\ sentinel to break out of the segment's local queue processing.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 036cf41bf6

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


# Signal completion for whole session because of error
await local_queue.put(VoiceStreamEventLifecycle(event="session_ended"))
await self._add_error(e)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve ordered audio before surfacing TTS errors

When a later TTS segment fails faster than an earlier segment, this puts the terminal error directly on the public queue instead of going through _ordered_tasks/_dispatch_audio. Because stream() stops as soon as it reads VoiceStreamEventError, consumers can miss already-started earlier audio and lifecycle events whenever concurrent segment synthesis completes out of order; route the terminal error through the ordered path, or otherwise wait for earlier local queues before publishing it.

AGENTS.md reference: AGENTS.md:L109-L110

Useful? React with 👍 / 👎.

Comment on lines +300 to 304
for task in all_tasks:
if task.done() and not task.cancelled():
if task.exception():
self._stored_exception = task.exception()
break

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Retrieve the producer exception before exiting

When a TTS segment fails during a pipeline run, the segment task and text_generation_task can both complete with exceptions because the producer re-raises the gather failure. This loop still scans the segment tasks first and breaks on the first exception, so it never calls exception() on text_generation_task; the original “Task exception was never retrieved” warning remains for that failure path. Continue retrieving all done task exceptions while only preserving the first stored exception.

AGENTS.md reference: AGENTS.md:L109-L110

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b0e7381b5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

await self._add_error(e)
await local_queue.put(None)
if self.text_generation_task is not None and not self.text_generation_task.done():
self.text_generation_task.cancel()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Let the producer finish its failure cleanup

When a TTS segment fails while the streamed multi-turn producer is between turns or waiting on transcription/workflow, this cancellation makes process_turns() enter its finally to close the STT session, but stream() has already received the public error and then _cleanup_tasks() can cancel the same producer again while transcription_session.close()/output._done() is awaited. That second cancellation can interrupt cleanup, leaving the STT connection or trace unfinished; let the producer run its finally to completion, or shield that cleanup, instead of canceling it from the TTS task.

AGENTS.md reference: AGENTS.md:L110-L110

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants