Skip to content

fix: transcription lock-up race condition & add small debounce#824

Merged
cjpais merged 7 commits intocjpais:mainfrom
johnpyp:fix/keybind-race-condition-and-debounce
Feb 16, 2026
Merged

fix: transcription lock-up race condition & add small debounce#824
cjpais merged 7 commits intocjpais:mainfrom
johnpyp:fix/keybind-race-condition-and-debounce

Conversation

@johnpyp
Copy link
Copy Markdown
Contributor

@johnpyp johnpyp commented Feb 14, 2026

Before Submitting This PR

Please confirm you have done the following:

  • I have searched existing issues and pull requests (including closed ones) to ensure this isn't a duplicate
    • There have been a couple attempts at this (1, 2), but this is a more readable/simpler implementation that covers more edge cases, both the signal handler + shortcut entrypoint.
  • I have read CONTRIBUTING.md

Human Written Description

Part 1 - Race condition fix

This PR started by using an atomic state machine, but it still felt pretty brittle. I realized the correct way to approach this was just by using a "coordinator" (aka actor pattern) which gets simple "notifications" from the rest of the system, and handles the incoming commands sequentially, naturally brokering access to the transcription state.

Note: This fix doesn't rely on the 30ms debounce, the debounce is just a UX thing as described below.

Other attempts to fix this only used an AtomicBool, which seems good on the surface, but doesn't truly fix the race condition because it either 1) still causes a lockup in some edge cases (conservative lock) or 2) has multiple pipelines processing at the same time, potentially pasting text interleaved and whatnot. If you only guard the keypress, then you can have multiple push to talk / keypresses accidentally queue up multiple transcriptions.

Basically, there's a myriad of edge case race conditions that get categorically eliminated by using an actor here :)

Part 2 - Debounce for UX

Second, this PR adds a 30ms debounce which just helps avoid sound effect spam and needless repeated processing (but again, this is NOT necessary for the correctness guarantee)

Related Issues/Discussions

Fixes #64
Fixes #707
Fixes #462 (closed, but not truly fixed)

Related attempts:

Previous related PR which attempted to mitigate this but caused the lock up accidentally:

Community Feedback

Issues linked above.

Testing

Tested on linux + wayland using SIGUSR2.

I have not tested this on macos, or using push to talk keybinds in the app. If someone else is able to test in those environments that'd be great, or we can merge and see how it works out. I don't think this would cause issues.

Screenshots/Videos (if applicable)

AI Assistance

  • No AI was used in this PR
  • AI was used (please describe below)

If AI was used:

  • Tools used: Claude Code
  • How extensively: I used Claude Code to explore the issue and verify the race condition, then instructed it on precisely how to fix the issue, and verified the fix. I stand by the fix.

@johnpyp johnpyp force-pushed the fix/keybind-race-condition-and-debounce branch 2 times, most recently from da72240 to e7822af Compare February 14, 2026 18:47
@johnpyp johnpyp force-pushed the fix/keybind-race-condition-and-debounce branch from e7822af to 1a04a56 Compare February 14, 2026 19:09
@johnpyp johnpyp marked this pull request as draft February 14, 2026 19:29
@johnpyp johnpyp force-pushed the fix/keybind-race-condition-and-debounce branch 2 times, most recently from 5c041cc to bca2a79 Compare February 14, 2026 20:00
@johnpyp johnpyp marked this pull request as ready for review February 14, 2026 20:05
@johnpyp
Copy link
Copy Markdown
Contributor Author

johnpyp commented Feb 14, 2026

Note: refactored to use an actor to be even more robust to future race conditions and be a lot easier to understand conceptually. The original state machine commit is still here.

@johnpyp johnpyp force-pushed the fix/keybind-race-condition-and-debounce branch from bca2a79 to c493d8c Compare February 14, 2026 20:09
@johnpyp johnpyp force-pushed the fix/keybind-race-condition-and-debounce branch from c493d8c to 00e7125 Compare February 14, 2026 20:16
@cjpais
Copy link
Copy Markdown
Owner

cjpais commented Feb 16, 2026

from the quick once over this seems like probably the best change, I'll take a further look

edit: I tested this on MacOS and it seems to work great for me

@cjpais cjpais merged commit 7fdde63 into cjpais:main Feb 16, 2026
4 checks passed
mceachen added a commit to mceachen/Handy that referenced this pull request Feb 20, 2026
Resolve merge conflicts with main's race condition fix (cjpais#824) and
structured outputs (cjpais#706), keeping both FinishGuard/TranscriptionCoordinator
and the new streaming VAD modes.
RohanMuppa pushed a commit to RohanMuppa/Handy that referenced this pull request Feb 21, 2026
…s#824)

* fix: add TranscriptionState to prevent race conditions & add debounce

* refactor(transcription): replace state machine with actor coordinator

* some cleanup

* format

* minor cleanup

* format

---------

Co-authored-by: CJ Pais <[email protected]>
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.

Modifier keys get stuck, requires logout to fix [BUG] Race -> crash Multiple Instances Allowed on Windows

2 participants