Fix crash when playing assistant audio on a non-rendering engine - #197
Merged
Conversation
AVAudioEngine.isRunning can report true while the engine's IO thread
never cycles (AUVPAggregate stream timeout, -10877). Calling
AVAudioPlayerNode.play() in that state raises an uncatchable NSException
("player did not see an IO cycle") and terminates the host app.
Require a valid sample time on the output node before scheduling and
playing, and drop the buffer with a log warning otherwise so callers
that rebuild dead capture graphs get the chance to recover.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A host app using the realtime voice stack crashed with:
AudioPCMPlayer.playPCM16Audioguards onaudioEngine.isRunningbefore callingAVAudioPlayerNode.play(), but that flag can report true while the engine's IO thread never actually cycles — theAUVPAggregatestream-timeout failure (-10877) already documented for the capture side, where the engine claims it started but the HAL stream never runs. Callingplay()on such an engine raises an uncatchable NSException and terminates the process, and it fires as soon as the first assistant audio delta arrives — before any capture-side watchdog gets a chance to rebuild the dead graph.Fix
Require evidence that the render loop is actually producing cycles — a valid sample time on the engine's output node — in addition to
isRunningbefore scheduling and playing:On a dead engine the buffer is now dropped with a log warning instead of crashing. Since capture shares the same engine, the mic stays digitally silent in that state, so callers that watchdog for dead capture (e.g. a self-healing controller that rebuilds the graph) can recover the session.
The guard moved to the top of the method, which also skips the base64 decode and format conversion work when the engine can't play, and the old
if audioEngine.isRunningwrapper around scheduling became redundant and was unwrapped (indentation-only change for that block).Testing
swift buildand package tests pass.🤖 Generated with Claude Code