Version: @livekit/agents 1.7.1 (also present on main)
What happens
FallbackAdapter.setupEventForwarding() (dist/tts/fallback_adapter.js) re-emits every child TTS error event verbatim:
tts.on("error", (error) => { this.emit("error", error); });
When the primary TTS fails with a non-retryable APIStatusError (e.g. ElevenLabs 401 payment_required), the child's stream emits { type: 'tts_error', recoverable: false }. FallbackChunkedStream.run / FallbackSynthesizeStream.run catch that same APIError, log "TTS failed, switching to next instance" and mark the instance unavailable — but the forwarded recoverable: false event has already reached AgentSession, which logs "AgentSession is closing due to an unrecoverable error" and tears the session down. The fallback instance never gets to speak.
Observed 2026-09-01 in production with ElevenLabs primary + Inworld fallback: 100% of calls dropped while ElevenLabs returned 401, despite the configured fallback.
Expected
While another instance in the chain is still available, a child's non-retryable error should be surfaced as recoverable (the adapter IS recovering), and only become unrecoverable once every instance has failed (the adapter already throws all TTS instances failed in that case).
Suggested fix
tts.on("error", (error) => {
const idx = this.ttsInstances.indexOf(tts);
const othersAvailable = this._status.some((s, i) => i !== idx && s.available);
if (othersAvailable && error && typeof error === "object" && error.recoverable === false) {
this.emit("error", { ...error, recoverable: true });
return;
}
this.emit("error", error);
});
(The LLM FallbackAdapter does not have this problem: it does not forward child errors; tryGenerate captures them.)
Version: @livekit/agents 1.7.1 (also present on
main)What happens
FallbackAdapter.setupEventForwarding()(dist/tts/fallback_adapter.js) re-emits every child TTSerrorevent verbatim:When the primary TTS fails with a non-retryable
APIStatusError(e.g. ElevenLabs401 payment_required), the child's stream emits{ type: 'tts_error', recoverable: false }.FallbackChunkedStream.run/FallbackSynthesizeStream.runcatch that sameAPIError, log "TTS failed, switching to next instance" and mark the instance unavailable — but the forwardedrecoverable: falseevent has already reachedAgentSession, which logs "AgentSession is closing due to an unrecoverable error" and tears the session down. The fallback instance never gets to speak.Observed 2026-09-01 in production with ElevenLabs primary + Inworld fallback: 100% of calls dropped while ElevenLabs returned 401, despite the configured fallback.
Expected
While another instance in the chain is still available, a child's non-retryable error should be surfaced as recoverable (the adapter IS recovering), and only become unrecoverable once every instance has failed (the adapter already throws
all TTS instances failedin that case).Suggested fix
(The LLM FallbackAdapter does not have this problem: it does not forward child errors;
tryGeneratecaptures them.)