Skip to content

fix(stt): reconnect on send-side socket drops in remaining plugins#6474

Open
flasun wants to merge 2 commits into
livekit:mainfrom
flasun:fix/stt-send-side-socket-drop-reconnect
Open

fix(stt): reconnect on send-side socket drops in remaining plugins#6474
flasun wants to merge 2 commits into
livekit:mainfrom
flasun:fix/stt-send-side-socket-drop-reconnect

Conversation

@flasun

@flasun flasun commented Jul 18, 2026

Copy link
Copy Markdown

fix(stt): reconnect on send-side socket drops in remaining plugins

What

Follow-up to #6429 (deepgram) and #6467 (assemblyai/baseten/speechmatics/elevenlabs/gladia/openai/inference), which fixed a class of bug where a send-side WebSocket drop crashed the STT session instead of reconnecting. This applies the same fix to the streaming STT plugins those PRs didn't cover.

Closes #6473.

Why

SpeechStream._main_task only reconnects on APIError; any other exception kills the stream. When the peer resets the socket mid-stream, ws.send_bytes()/send_str() raises a raw aiohttp.ClientConnectionResetError (a ConnectionResetError), which isn't an APIError. In these plugins the send_task doesn't wrap the send loop, so that raw exception escapes and permanently terminates the stream — dead air, no failover. The recv path in each of these plugins already turns an unexpected close into the retryable APIStatusError (or an internal reconnect), so the fix just makes the send path symmetric.

Changes

Wrap each plugin's send_task loop in except (aiohttp.ClientError, ConnectionError), mirroring the plugin's existing recv idiom:

  • Raise-style (recv raises APIStatusError → send raises the retryable APIConnectionError): xai, fireworksai, smallestai, telnyx, slng, gradium. The guard (closing_ws / self._session.closed) matches each plugin's own recv guard, so a graceful close or a session teardown returns quietly rather than raising.
  • Internal-reconnect style (recv sets _reconnect_event and returns): simplismart — the send path now triggers the same internal reconnect instead of crashing.
  • fireworksai and gradium additionally import APIConnectionError.

Test

tests/test_plugin_gradium_stt.py::test_send_side_socket_drop_raises_api_connection_error drives SpeechStream._run with a fake WebSocket whose first audio send_str drops the socket, and asserts _run raises APIConnectionError (retryable) rather than the raw ClientConnectionResetError. Verified it fails on the unpatched code and passes with the fix. gradium is used as the representative case since it already has a plugin test module.

Notes

Follow-up to livekit#6429 and livekit#6467: wrap the send_task loop in the streaming STT
plugins those PRs didn't cover, so a mid-send socket drop raises a retryable
APIConnectionError (or triggers the plugin's internal reconnect) instead of a
raw ConnectionResetError that permanently kills the session with no failover.

The recv path in each of these plugins already turns an unexpected close into
a retryable error; this makes the send path symmetric. Guard conditions
(closing_ws / self._session.closed) mirror each plugin's own recv handling.

Plugins: xai, fireworksai, simplismart, smallestai, telnyx, slng, gradium.
Adds a regression test in tests/test_plugin_gradium_stt.py.
@flasun
flasun requested a review from a team as a code owner July 18, 2026 02:07
@CLAassistant

CLAassistant commented Jul 18, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

devin-ai-integration[bot]

This comment was marked as resolved.

slng's _run keys its same-endpoint immediate-retry logic on
isinstance(exc, APIStatusError); any other error triggers a permanent
failover to a lower-priority endpoint. recv_task already raises
APIStatusError on an unexpected close, so the send guard must too —
otherwise a transient send-side blip permanently downgrades the provider
for the rest of the session. Addresses the review finding on slng.
@flasun

flasun commented Jul 18, 2026

Copy link
Copy Markdown
Author

Good catch from the automated review on `slng` — thanks. Unlike the other six plugins (which propagate to the base `_main_task`, where any `APIError` is retried on the same connection), `slng` has custom per-endpoint failover that only grants same-endpoint immediate retries for `APIStatusError`; any other error permanently bumps `_active_endpoint_index`. Since `recv_task` already raises `APIStatusError` on an unexpected close, a send-side drop raising `APIConnectionError` would have permanently downgraded to a lower-priority endpoint instead of retrying the primary.

Fixed in c32c19d: the `slng` send guard now raises `APIStatusError` (matching `recv_task`), so both paths get identical same-endpoint-retry-then-failover semantics. Verified the other six do not have this type-keyed failover logic, so their `APIConnectionError` remains correct.

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.

STT plugins still crash the session on send-side WebSocket drops after #6429/#6467

2 participants