Skip to content

fix(core): don't mark FallbackAdapter primary unavailable on empty LLM turns#1455

Open
ubinatus wants to merge 1 commit into
livekit:mainfrom
ubinatus:fix/fallback-adapter-empty-token-stream
Open

fix(core): don't mark FallbackAdapter primary unavailable on empty LLM turns#1455
ubinatus wants to merge 1 commit into
livekit:mainfrom
ubinatus:fix/fallback-adapter-empty-token-stream

Conversation

@ubinatus
Copy link
Copy Markdown

Closes #1454.

Problem

FallbackSynthesizeStream throws APIConnectionError("TTS stream completed but no audio was received") and triggers markUnAvailable + cascade to the next provider on turns where the LLM emits zero spoken text — e.g., a turn whose content is a tool call with no text.

In agents/src/tts/fallback_adapter.ts, forwardBufferToTTS may consume the LLM stream and forward only sentinel tokens without ever calling stream.pushText(...). The provider correctly returns no audio because nothing was sent to synthesize. The adapter then hits if (!sawRawAudio) and throws, which marks the first provider unavailable, retries through the rest of the chain, and ultimately raises all TTS instances failed. In production this manifests as recurring spurious failover storms and skewed availability metrics on agents that use tools.

Fix

Track non-sentinel tokens received by readInputLLMStream (realTokensReceived). In the !sawRawAudio branch, await the input LLM stream so the count is settled, then:

  • if realTokensReceived === 0, emit SynthesizeStream.END_OF_STREAM downstream and return cleanly,
  • otherwise throw as before (silent failures with real text still trigger fallback).

The signal is taken from the input side rather than the forward side because the forward task can exit early via streamOutputCompleted / aborted when the inner stream fails, masking text that was actually sitting in the buffer.

Test

Added should not mark the primary unavailable when the LLM emits zero text tokens: closes the adapter's input stream without enqueuing any text and asserts both providers remain available and no frames were produced.

Full agents/ suite is green (762 passed, 2 skipped).

Affected version

@livekit/agents@1.4.0 (same code path in 1.3.x).

…M turns

When the LLM emits a turn with zero spoken text (e.g. a tool-only turn),
`FallbackSynthesizeStream` would throw `APIConnectionError("TTS stream
completed but no audio was received")`, mark the first provider
unavailable, and cascade through the fallback chain — ultimately raising
`all TTS instances failed`. The empty audio is the correct response
because nothing was sent to synthesize.

Track non-sentinel tokens received from the input LLM stream. In the
`!sawRawAudio` branch, await the input stream so the count is settled
and, if zero real tokens were received, treat the empty result as a
clean no-op exit (emit END_OF_STREAM downstream and return) instead of
throwing. Silent failures with real text still trigger fallback.

Adds a unit test that closes the input without enqueuing any text and
asserts no provider is marked unavailable.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 11, 2026

🦋 Changeset detected

Latest commit: 2239c17

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 31 packages
Name Type
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugins-test Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch

Not sure what this means? Click here to learn what changesets are.

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

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 11, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown
Contributor

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

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 3 additional findings.

Open in Devin Review

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.

FallbackAdapter throws "no audio received" on tool-only LLM turns (empty token stream)

2 participants