macOS: client traps (SIGTRAP/dispatch_assert_queue) in OSXKeyState::pollActiveGroup() on forwarded key events — TIS main-queue assert, still unguarded on master
Deskflow version info
Deskflow: 1.26.0.0
Qt: 6.10.1
System: macOS 26.5.2 (25F84), Apple Silicon (Mac16,10)Build types
- Official release (installer/package from GitHub releases)
Deskflow configuration
- Windows server, macOS 26.5.2 client (Mac mini, Apple Silicon)
- Client runs headless as a LaunchAgent (
org.deskflow.client,KeepAlive=true), GUI not required - Single monitor per machine
- Both systems report language
tr(local languages: tr/remote languages: tr)
What steps will reproduce the problem?
- Windows server, macOS client, both with a Turkish (non-ABC) input source active.
- Connect the macOS client to the server.
- Switch to the macOS screen and type on the Windows keyboard.
deskflow-coretraps withEXC_BREAKPOINT (SIGTRAP). Because the client is a LaunchAgent withKeepAlive=true, launchd immediately respawns it, so the visible symptom is "the keyboard dies for a second and comes back" rather than an app disappearing.
The crash is intermittent, not every keystroke — it fires when HIToolbox needs to refresh its input-source list rather than serve it from cache. In one session it crashed 3 times within 3 minutes (19:15, 19:17, 19:17), then ran fine for 2 minutes.
The crash
Two distinct call sites, same root cause. The first one is not covered by #9924 — it is on the client, in the keystroke path:
A) OSXKeyState::pollActiveGroup() via KeyState::fakeKeyDown() — on every forwarded key event
Thread 5 CRASHED:
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 deskflow-core OSXKeyState::pollActiveGroup() const + 40
7 deskflow-core KeyState::fakeKeyDown(unsigned int, unsigned int, unsigned short, std::string const&) + 252
8 deskflow-core ServerProxy::parseMessage(unsigned char const*) + 980
9 deskflow-core ServerProxy::handleData() + 188
10 deskflow-core EventQueue::dispatchEvent(Event const&) + 332
11 deskflow-core StreamFilter::filterEvent(Event const&) + 84
12 deskflow-core EventQueue::dispatchEvent(Event const&) + 332
13 deskflow-core EventQueue::loop() + 344
14 deskflow-core App::runEventsLoop(void const*) + 24
15 deskflow-core Thread::threadFunc(void*) + 128
16 deskflow-core ArchMultithreadPosix::doThreadFunc(ArchThreadImpl*) + 76
17 deskflow-core ArchMultithreadPosix::threadFunc(void*) + 56
18 libsystem_pthread _pthread_start + 136
19 libsystem_pthread thread_start + 8B) AppUtilUnix::getKeyboardLayoutList() via ServerProxy construction — on connect
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 TSMGetInputSourceCountWithFilteredAdditions + 40
5 HIToolbox TISCreateInputSourceList + 100
6 deskflow-core AppUtilUnix::getKeyboardLayoutList() + 132
7 deskflow-core ServerProxy::ServerProxy(Client*, deskflow::IStream*, IEventQueue*)Variant B is the client-side mirror of #9924 (which was on the server, via ClientProxy1_8::synchronizeLanguages()).
Thread 0 is parked in -[NSApplication run] (runCocoaApp) in both cases — the main queue is alive and idle, the TIS call is simply made from EventQueue::loop()'s thread.
Why I'm filing this rather than commenting on #9924
#9924 was closed as not planned / cannot reproduce on macOS 27 beta 3. This report differs on three points:
- It reproduces on shipping macOS 26.5.2 (25F84), not a 27 beta. The main-queue enforcement on the TIS/TSM path is not exclusive to macOS 27.
- Call site A is new. #9924 only covers
getKeyboardLayoutList().OSXKeyState::pollActiveGroup()reachesTSMGetInputSourcePropertythroughisValidateInputSourceRef, and sits directly infakeKeyDown()— so it is hit on forwarded key events, not just at connection time. FixingAppUtilUnix.cppalone would leave this path crashing. - It is the client side; #9924 was the server side.
@amagus — in #9924 you asked for a full all-thread stack trace. Full .ips reports for 7 crashes are attached below; happy to provide a debug build trace or any other data.
Additional information
- Not a regression — present since first use. This is a clean install, not an upgrade: Deskflow 1.26.0.0 was installed 2026-09-05 00:42 and the first crash is 2026-09-05 03:56, ~3 hours later. Both
Deskflow.confandDeskflow.conf.bakrecordlastVersion=1.26.0.0, so no earlier version ever ran here and I cannot claim an earlier release behaved differently. The macOS install itself has been unchanged since 2026-06-25 (SystemVersion.plistmtime;InstallHistory.plistshows only XProtect updates since), so this is not an OS-update side effect either. - Still unfixed on
master. Ata930c87ea4ec,OSXKeyState::pollActiveGroup()still guards itsTISCopyCurrentKeyboardLayoutInputSource()/TISGetInputSourceProperty()pair withg_tisMutexalone. A mutex serialises Deskflow's own threads but cannot satisfydispatch_assert_queue(main_queue), so the failing call is unchanged since the version I am running. The 2026-05-08 commit "fix(mac): protect TIS API calls with a mutex (they are not thread safe)" addresses thread-safety, which is a different constraint from the queue-affinity one that traps here. - 7 crash reports, all
EXC_BREAKPOINT (SIGTRAP)/dispatch_assert_queue: 4 on 2026-09-05 (2× variant B, 2× variant A), 3 on 2026-09-07 (all variant A). - Does it stop me using Deskflow? Not entirely —
KeepAlivemasks it — but typing on the Mac from the Windows keyboard is unreliable. - Restarting the client clears it only until the next refresh of the input-source list.
- Note on the sanity checks: this is the official 1.26.0.0 release and I have not run a continuous build. Rather than guess, I read the current source instead — see the
masternote above; the crashing call site is unchanged, so a newer build should not be expected to behave differently. Happy to run one and confirm if that would still be useful. - On the suggested fix — I note @amagus' comment in #9924 that "using the main queue is not an option on several of the TIS use cases". Call site A runs per-keystroke, so a
dispatch_syncto the main queue there would also be a latency concern. Caching the active group and invalidating it from a main-queuekTISNotifySelectedKeyboardInputSourceChangedobserver may fit better than dispatching per keystroke.
Show log
[2026-09-07T19:17:42.165] NOTE: configured 1 server address(es)
[2026-09-07T19:17:42.179] NOTE: started client
[2026-09-07T19:17:42.179] IPC: connecting to '<server>': <server>:24800
2026-09-07 19:17:42.206 deskflow-core[27315:520335] starting cocoa loop
[2026-09-07T19:17:42.249] IPC: peer fingerprint: <redacted>
[2026-09-07T19:17:42.250] INFO: connected to secure socket
[2026-09-07T19:17:42.250] INFO: server tls certificate info: /CN=Deskflow
[2026-09-07T19:17:42.250] INFO: network encryption protocol: TLSv1.3
[2026-09-07T19:17:42.270] INFO: local languages: tr
[2026-09-07T19:17:42.275] INFO: remote languages: tr
[2026-09-07T19:17:42.281] WARNING: cursor may not be visible
[2026-09-07T19:17:42.283] IPC: connected to server
[2026-09-07T19:17:55.316] INFO: entering screen
[2026-09-07T19:17:55.319] INFO: clipboard was updated
[2026-09-07T19:17:55.321] INFO: clipboard was updated
<-- crash here, launchd respawns
initial settings file: /Users/<user>/Library/Deskflow/Deskflow.conf
[2026-09-07T19:17:59.569] NOTE: configured 1 server address(es)
[2026-09-07T19:17:59.583] NOTE: started client
[2026-09-07T19:17:59.584] IPC: connecting to '<server>': <server>:24800
2026-09-07 19:17:59.609 deskflow-core[27316:520671] starting cocoa loop
[2026-09-07T19:17:59.651] INFO: connected to secure socket
[2026-09-07T19:17:59.676] INFO: local languages: tr
[2026-09-07T19:17:59.681] INFO: remote languages: tr
[2026-09-07T19:17:59.688] IPC: connected to server
[2026-09-07T19:18:27.470] INFO: entering screenSource: deskflow/deskflow