Skip to content

fix(chat): ignore a stale voice recognizer's async callbacks after restart - #6494

Merged
pedrofrxncx merged 1 commit into
mainfrom
fix/voice-input-stale-recognizer-w1
Aug 25, 2026
Merged

fix(chat): ignore a stale voice recognizer's async callbacks after restart#6494
pedrofrxncx merged 1 commit into
mainfrom
fix/voice-input-stale-recognizer-w1

Conversation

@pedrofrxncx

@pedrofrxncx pedrofrxncx commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Bug found while reading recently-touched apps/web/src/hooks/use-voice-input.ts (churned by #6451).

Failure scenario: cancelRecording() calls recognitionRef.current?.abort() but does not null out the ref, and startRecording() right after creates a new SpeechRecognition instance, reassigning recognitionRef.current. Browsers (Chrome) resolve abort() asynchronously as an error event with error: "aborted" — so the old, already-cancelled recognizer's onerror can fire after the new one is already recording. Its handler unconditionally does isRecordingRef.current = false; recognitionRef.current = null; setStatus(...), which kills the ref to the live recognizer and flips the UI status back to idle/permission-denied mid-recording. The same stale-closure issue affects onend, which could restart a dead recognizer instance that no longer holds the mic session.

Fix: each handler now checks recognitionRef.current !== recognition and bails if the callback belongs to a superseded instance — the same pattern already used to distinguish the live session from historic ones.

Regression test: use-voice-input.test.ts — starts recording, cancels, restarts (second SpeechRecognition instance), then fires the first instance's onerror({error: "aborted"}). Before the fix this drops status to idle even though the second recognizer is still recording; after the fix status stays recording. Verified the test fails on the pre-fix code (git stash the source file, rerun) and passes after.

To confirm: bun test apps/web/src/hooks/use-voice-input.test.ts

Checked locally: bun run fmt, cd apps/web && bunx tsc --noEmit (no new errors), bunx oxlint on both changed files (0 warnings/errors), and the targeted test file above. Full CI covers the rest.


Summary by cubic

Prevents stale SpeechRecognition callbacks from affecting the current session after a restart. Previously, a cancelled recognizer’s late onerror("aborted") or onend could null out the live recognitionRef, flip status from recording, or restart a dead instance; now those callbacks are ignored if they don’t belong to the current recognizer.

  • Handlers in use-voice-input.ts early-return when recognitionRef.current !== recognition.
  • Adds a regression test that cancels, restarts, then fires the old instance’s onerror("aborted"), asserting status remains recording.

Written for commit 52cec91. Summary will update on new commits.

Review in cubic

@pedrofrxncx
pedrofrxncx merged commit f6ba7c5 into main Aug 25, 2026
34 checks passed
@pedrofrxncx
pedrofrxncx deleted the fix/voice-input-stale-recognizer-w1 branch August 25, 2026 02:02
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.

1 participant