fix(chat): stop the voice-input recognizer from busy-looping on a fatal error - #6451
Merged
Merged
Conversation
…al error Web Speech recognition had no onerror handler, so onend's auto-restart (used to keep dictation going through routine no-speech gaps) would also kick in after a fatal error - a dropped network connection, the mic disconnecting, or the OS revoking mic permission mid-recording. Each restart immediately re-fails with the same error, so the hook busy-loops indefinitely while the UI stays stuck showing 'recording' with no way out but a page reload. Add an onerror handler: no-speech is left alone (onend already restarts it, this is normal in continuous mode), anything else stops the recognizer and returns status to idle/permission-denied instead of retrying forever.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug found while reviewing the chat input area for error resilience under network hiccups:
useVoiceInput(used by the chat composer's mic button,apps/web/src/components/chat/input.tsx) never attached anonerrorhandler to the Web SpeechSpeechRecognitioninstance.recognition.onendrestarts the recognizer wheneverisRecordingRef.currentis still true — that's needed so continuous dictation survives the routine gaps between phrases. But with noonerrorhandler, the same restart also fires after a fatal error: a dropped network connection (mostSpeechRecognitionimplementations are cloud-backed), the mic being unplugged, or the OS revoking mic permission mid-recording. Each restart immediately re-fails with the identical error, so the hook busy-loops indefinitely and the UI is stuck showing "recording" with no way out short of a page reload.Fix: add an
onerrorhandler.no-speechis left alone (it's routine in continuous mode,onendalready restarts it correctly). Any other error code stops the recognizer, tears down the visualizer, and returnsstatustoidle(orpermission-deniedfornot-allowed) instead of retrying forever.Failure scenario: start voice input, then simulate a
network/audio-capture/not-allowederror from the recognizer — before this fixstatusstaysrecordingforever andrecognition.start()gets called again on everyonend; after the fix it settles toidleand stops restarting. Covered by the new teststops recording instead of busy-looping restarts on a fatal recognition errorinapps/web/src/hooks/use-voice-input.test.ts.Reviewer check:
bun test apps/web/src/hooks/use-voice-input.test.ts(4 pass),cd apps/web && bunx tsc --noEmit,bunx oxlint apps/web/src/hooks/use-voice-input.ts apps/web/src/hooks/use-voice-input.test.ts— all green locally; full CI covers the rest.Summary by cubic
Stops the chat mic’s voice input from busy-looping after a fatal Web Speech error by handling
SpeechRecognition.onerror. Previouslyonendalways restarted recognition and left status stuck at recording; now we ignoreno-speech, stop on other errors, tear down the visualizer, and set status to idle or permission-denied.recognition.onerror: ignoreno-speech; for other errors setstatustoidleorpermission-denied(not-allowed), clearrecognitionRef, stop the visualizer, and preventonendfrom auto-restarting.onendstill restarts only when recording continues and no fatal error occurred.apps/web/src/hooks/use-voice-input.test.ts.Written for commit 8e2e04b. Summary will update on new commits.