Repository navigation
fix(deepgram): guard empty languages list in multilingual STT decoding - #7976
Speedyzyzz wants to merge 3 commits into
Conversation
… decoding
- In live_transcription_to_speech_data: check alt.get('languages') instead of 'languages' in alt to avoid IndexError when Deepgram returns an empty languages list for multilingual live transcription.
- In prerecorded_transcription_to_speech_event: guard against empty results.channels by returning an empty SpeechEvent with alternatives=[] instead of crashing on channel 0 indexing. Guard nested word timing dictionary access with safe get() defaults.
- Add unit test coverage in tests/test_plugin_deepgram_stt.py for empty languages list in live transcription and empty channels list in prerecorded transcription.
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
🔍 Devin Review: 1 flag
Not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| request_id = data["metadata"]["request_id"] | ||
| channel: dict = data["results"]["channels"][0] | ||
| dg_alts = channel["alternatives"] | ||
| request_id = data.get("metadata", {}).get("request_id", "") |
There was a problem hiding this comment.
_recognize_impl does not check the response status, so today an error body (for example a 401) fails on data["metadata"] and becomes an APIConnectionError. With the .get() defaults, that error body returns an empty FINAL_TRANSCRIPT, and a bad API key looks like silence to the caller and to FallbackAdapter.
Could you:
- Keep the
languagesfix. - Revert the
prerecorded_transcription_to_speech_eventchanges. If you want to handle errors there, checkres.statusin_recognize_impland raiseAPIStatusErrorwith the body instead.
There was a problem hiding this comment.
Good catch. Masking a 401 error payload as an empty FINAL_TRANSCRIPT would break FallbackAdapter failover. I have reverted the prerecorded_transcription_to_speech_event changes entirely and kept only the defensive check on the empty languages list for multilingual live transcription.
There was a problem hiding this comment.
Devin Review found 3 new potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
There was a problem hiding this comment.
🔴 Silent recordings fail prerecorded recognition
When Deepgram returns no channels, prerecorded_transcription_to_speech_event raises IndexError. The exception becomes APIConnectionError, so silent recordings fail instead of returning an empty transcript.
(Refers to this code)
Learn more
The prerecorded converter reads the first channel before it builds a speech event. A response with no channels raises IndexError, which _recognize_impl converts to APIConnectionError instead of returning a final transcript.
Example: For {"metadata": {"request_id": "req-1"}, "results": {"channels": []}}, recognition fails rather than returning FINAL_TRANSCRIPT with no alternatives.
Recommended fix: Check for an empty channel list before selecting the first channel; return a final speech event with the request ID and empty alternatives. Restore coverage for this response.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
🟡 Null detected language aborts recognition
When Deepgram sends detected_language: null, LanguageCode(None) raises before prerecorded_transcription_to_speech_event processes alternatives. Even an empty alternatives list becomes a failed recognition request.
(Refers to this code)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
🟡 Missing word timing aborts recognition
When a prerecorded word lacks start or end, prerecorded_transcription_to_speech_event raises KeyError. TimedString accepts missing timings, but the alternative's direct lookups fail the entire request.
(Refers to this code)
Learn more
The converter uses default timing values for individual TimedString words, but its speech-level timing accesses the first and last word directly. A missing endpoint raises KeyError, and _recognize_impl reports it as a connection error.
Example: An alternative with words: [{"word": "hello", "end": 0.5}], confidence: 0.8, and transcript: "hello" fails because its first word has no start; the word-level decoder would otherwise use zero.
Recommended fix: Use the same .get(..., 0) defaults for speech-level timing as for each TimedString and cover missing first/last word timings in tests.
Was this helpful? React with 👍 or 👎 to provide feedback.
Problem
When using Deepgram multilingual live transcription (
language="multi"), Deepgram can return an emptylanguages: []list in alternatives (e.g. during low confidence audio, non speech segments, or interim silence). The checkif language == "multi" and "languages" in alt:evaluates to True, but indexingalt["languages"][0]immediately raisesIndexError: list index out of range, which abruptly aborts the_run()background receive task.Solution
Check truthiness
alt.get("languages")instead of key containment"languages" in altinlive_transcription_to_speech_data.Tests
test_live_transcription_empty_languages_multilingualintests/test_plugin_deepgram_stt.pyverifying that an emptylanguages: []list processes without raisingIndexError.tests/test_plugin_deepgram_stt.pypass cleanly in 0.59s.ruff checkandruff formatare clean.