Skip to content

fix(stt): detect half-open websockets in OpenAI, ElevenLabs, and Inworld STT plugins - #1

Closed
ninglinLiu wants to merge 4 commits into
mainfrom
cursor/fix-halfopen-websocket-stt-193a
Closed

ninglinLiu wants to merge 4 commits into
mainfrom
cursor/fix-halfopen-websocket-stt-193a

Conversation

@ninglinLiu

Copy link
Copy Markdown
Owner

Problem

OpenAI, ElevenLabs, and Inworld streaming STT plugins never notice a half-open websocket (provider/NAT gone without FIN/RST). They wait forever on ws.receive(), so FallbackAdapter never fails over. Deepgram, Soniox, and similar plugins already have fixes that pass heartbeat=30.0 to ws_connect and map WSMsgType.ERROR to retryable APIConnectionError (see merged fixes like livekit#7206).

Solution

This PR applies the same pattern to OpenAI realtime STT, ElevenLabs STT, and Inworld STT:

  1. Added heartbeat=30.0 to ws_connect in all three plugins

    • Without heartbeat, a silently dropped socket (half-open TCP connection, no FIN/RST) is never noticed
    • recv_task parks on ws.receive() forever and the reconnect loop never runs
  2. Handle WSMsgType.ERROR by raising APIConnectionError

    • The heartbeat closes the socket when a ping goes unanswered, surfacing as WSMsgType.ERROR
    • ws.exception() is the only place the reason survives, so we chain it to the APIConnectionError
    • This triggers reconnection or FallbackAdapter failover
  3. Added regression tests for each plugin proving they:

    • Pass heartbeat=30.0 to ws_connect
    • Notice a silent websocket via heartbeat timeout and raise retryable APIConnectionError
    • Reconnect within bounded time instead of hanging indefinitely

Testing

All new tests pass:

  • tests/test_plugin_openai_stt_halfopen.py ✅
  • tests/test_plugin_elevenlabs_stt_halfopen.py ✅
  • tests/test_plugin_inworld_stt_halfopen.py ✅

Existing unit tests still pass with no regressions.

Linter (ruff) checks pass for all modified files.

Changes

Modified files:

  • livekit-plugins/livekit-plugins-openai/livekit/plugins/openai/stt.py
  • livekit-plugins/livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
  • livekit-plugins/livekit-plugins-inworld/livekit/plugins/inworld/stt.py

New test files:

  • tests/test_plugin_openai_stt_halfopen.py
  • tests/test_plugin_elevenlabs_stt_halfopen.py
  • tests/test_plugin_inworld_stt_halfopen.py

Fixes livekit#7900


Note: This fix follows the same pattern as the merged Deepgram/Soniox/etc fixes. I kept the heartbeat inline in each plugin rather than creating a shared helper, as suggested in the issue discussion, to avoid expanding the scope.

Open in Web Open in Cursor 

omChauhanDev and others added 4 commits October 16, 2026 23:12
…rld STT plugins

Add heartbeat=30.0 to ws_connect and handle WSMsgType.ERROR by raising
retryable APIConnectionError in OpenAI realtime, ElevenLabs, and Inworld
STT plugins. Without heartbeat, a silently dropped socket (half-open TCP
connection, no FIN/RST) is never noticed: recv_task parks on ws.receive()
forever and the reconnect loop, which is exception-driven, never runs.

The heartbeat closes the socket when a ping goes unanswered, and that
surfaces as WSMsgType.ERROR. ws.exception() is the only place the reason
survives, so we chain it to the APIConnectionError.

Add regression tests for each plugin proving they notice a silent websocket
via heartbeat and raise retryable APIConnectionError within bounded time.

Fixes livekit#7900

Co-authored-by: NingLin Liu <x07514475479@gmail.com>
Run ruff format on 4 files per upstream CI requirements:
- livekit-plugins-elevenlabs/livekit/plugins/elevenlabs/stt.py
- tests/test_plugin_openai_stt_halfopen.py
- tests/test_plugin_elevenlabs_stt_halfopen.py
- tests/test_plugin_inworld_stt_halfopen.py

Restructured the Inworld test's _stream function to keep the
type: ignore[arg-type] comment on the http_session argument line
where mypy will apply it, while satisfying ruff format requirements.

Co-authored-by: NingLin Liu <x07514475479@gmail.com>
@ninglinLiu

Copy link
Copy Markdown
Owner Author

Superseded by upstream livekit#7912.

@ninglinLiu ninglinLiu closed this Oct 17, 2026
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.

Streaming STT: openai, elevenlabs and inworld never notice a silently dropped websocket, and the fixed plugins take ~45s

4 participants