Skip to content

Crash: dictation hotkey listener calls TSMGetInputSourceProperty off main thread (rdev / macOS 26) #2677

@constMONUMENT

Description

@constMONUMENT

Crash: dictation hotkey listener calls TSMGetInputSourceProperty off main thread (EXC_BREAKPOINT in voice-hotkey thread)

Summary

OpenHuman 0.54.0 crashes on macOS 26.4.1 (Apple Silicon) within ~90 seconds of launch whenever [dictation] enabled = true. The crash fires on any key press once the dictation hotkey listener is armed. Root cause is rdev::macos::keyboard::Keyboard::string_from_code calling TSMGetInputSourceProperty from a background thread; macOS 26 enforces that this API runs on the main dispatch queue and aborts the process via dispatch_assert_queue_fail when it does not.

Environment

  • App: OpenHuman 0.54.0 (CFBundle 0.54.0)
  • macOS: 26.4.1 (build 25E253), Apple Silicon (Mac17,9 / M5 Pro)
  • Config: [dictation] enabled = true, hotkey = "Fn", activation_mode = "push"
  • STT: stt_provider = "whisper", whisper_in_process = true, stt_model_id = "ggml-small.en-q5_1.bin" (locally downloaded)
  • Voice server: [voice_server] auto_start = false (dormant; not involved)
  • Screen intelligence: enabled = false (also confirmed not involved)

Reproduction

  1. Edit ~/.openhuman/users/<id>/config.toml: set [dictation] enabled = true
  2. Launch OpenHuman
  3. Press any key on the keyboard within ~90 seconds of launch
  4. App terminates with SIGTRAP (EXC_BREAKPOINT)

100% reproducible on the test system. Setting [dictation] enabled = false eliminates the crash.

Crash signature

Thread 64 Crashed:: voice-hotkey
0   libdispatch.dylib        _dispatch_assert_queue_fail + 120
1   libdispatch.dylib        dispatch_assert_queue$V2.cold.1 + 116
2   libdispatch.dylib        dispatch_assert_queue + 108
3   HIToolbox                islGetInputSourceListWithAdditions + 160
4   HIToolbox                isValidateInputSourceRef + 88
5   HIToolbox                TSMGetInputSourceProperty + 36
6   OpenHuman                rdev::macos::keyboard::Keyboard::string_from_code + 64
7   OpenHuman                rdev::macos::listen::raw_callback + 492
8   SkyLight                 processEventTapData + 556
...
17  OpenHuman                rdev::macos::listen::listen + 284

Exception: EXC_BREAKPOINT (SIGTRAP), codes 0x1, 0x18c7d64fc (the brk 1 inside _dispatch_assert_queue_fail).

Termination reason: Namespace SIGNAL, Code 5, Trace/BPT trap: 5.

Root cause

rdev (Narsil/rdev) registers a Quartz event tap and invokes a Rust callback (raw_callback) on the SkyLight event-dispatch thread. Inside that callback, Keyboard::string_from_code calls TISCopyCurrentKeyboardInputSource / TSMGetInputSourceProperty to resolve the keycode to a printable string. As of recent macOS releases (definitely 26.x), the TextInputSources API contains dispatch_assert_queue(dispatch_get_main_queue()) and the process is killed when invoked off the main thread.

This is a well-known rdev limitation; see for example rdev issues tagged "macOS" / "thread" upstream.

Suggested fixes (in order of preference)

  1. Replace rdev with tauri-plugin-global-shortcut or global-hotkey for the dictation listener. Both crates dispatch the hotkey callback on the Tauri/main thread and avoid the off-main TSM call entirely.
  2. Cache keycode→string mappings at startup on the main thread and look up from the rdev callback thread. The keyboard layout rarely changes during a session; invalidate the cache on kTISNotifySelectedKeyboardInputSourceChanged (delivered on the main run loop).
  3. Wrap the TSM call in dispatch_sync(dispatch_get_main_queue(), ...) inside rdev (or in OpenHuman's rdev integration shim). This is the smallest patch but adds latency to every key event.
  4. Upstream a fix to rdev if no maintained fork already has one.

Option 1 is the most robust and removes a dependency that has caused multiple cross-platform headaches historically.

Workaround

Set [dictation] enabled = false in config.toml. The app launches and runs normally; only the global dictation hotkey is lost. In-app voice surfaces appear unaffected.

Attached artifacts

  • Full .ips crash report (from ~/Library/Logs/DiagnosticReports/OpenHuman-2026-05-26-023634.ips) — available on request, redacted of system identifiers.
  • Sanitized config.toml showing the triggering setting — available on request.

Why this matters

[dictation] enabled = true is the documented path for the headline voice feature. Until this is fixed, every macOS 26 user who enables dictation hits a crash loop within seconds of launch, with no in-app indication of cause (logs show clean dictation startup, then process exit). The user-facing impact is "app quits unexpectedly" with no actionable signal.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions