#2165·kanata

Bug(macos): mouse event tap fails to install when kanata starts at boot, and never retries

Author: MadPigeonCreated Aug 21, 2026Updated Sep 7, 2026
Labelsbugmacos

Requirements

Describe the bug

On macOS, CGEventTapCreate fails when launchd starts kanata at boot from a LaunchDaemon. The same binary, the same config and the same permissions succeed when the service starts from a logged-in terminal. Kanata logs one error, clears its install claim, and continues to run without the tap for the rest of the process lifetime.

The error text names a cause that is not the cause:

Failed to create mouse event tap. Ensure kanata has Accessibility or Input Monitoring permission in System Settings > Privacy & Security.

Input Monitoring is granted. In the same log, 100 ms earlier, kanata grabbed the keyboard through the same permission. CGEventTapCreate returns a bare Err(()) for every reason, so the message states a cause that kanata cannot know.

The failure branch is src/oskbd/macos.rs:1932 on main (565d507). It stores MOUSE_TAP_INSTALLED = false with the comment "so a future live reload can retry once the user grants permission". A boot LaunchDaemon gets no live reload, so nothing retries. The tap stays dead until somebody restarts the service.

Effect: cfg_samples/kanata.plist sets RunAtLoad, and docs/setup-macos.md step 6 recommends it. Every user of that setup loses tap-dependent mouse behavior after every reboot, and gets it back the moment they restart kanata to look into it. That makes the report hard to reproduce for the person who files it.

The window server is reachable at the moment of the failure, so this is not the daemon being locked out of the GUI session. My build logs one added probe line before the tap install. It calls CGGetActiveDisplayList and CGEvent::new(CGEventSource::new(CombinedSessionState))?.location(). Both work in the boot daemon: one display, real cursor coordinates. An independent root LaunchDaemon with RunAtLoad, built only against CoreGraphics, sees the same from 15 seconds after boot. So only the tap creation fails.

Relevant kanata config

;; Minimal reproducer for `main`: the tap installs only when defsrc has
;; mouse buttons or wheel, or when mouse-movement-key is set.
(defsrc
  a mlft
)
(deflayer base
  a mlft
)

To Reproduce

  1. Install the sample LaunchDaemon as docs/setup-macos.md step 6 describes. Keep RunAtLoad set to true.
  2. Grant Input Monitoring to the kanata binary.
  3. Use a config that installs the mouse event tap. On main that means mouse buttons or wheel in defsrc, or a mouse-movement-key.
  4. Reboot the machine.
  5. Read /var/log/kanata.log after login. The tap install fails.
  6. Run sudo launchctl kickstart -k system/dev.kanata.kanata from a logged-in terminal, then read the log again. The tap installs and works.

Three boot starts, three failures. Every terminal-started run in the same log succeeds.

Expected behavior

Kanata retries the tap creation instead of giving up after one attempt. A retry with backoff makes the daemon self-heal once the GUI session is up, and it also covers the permission case that the current message describes.

Two smaller points:

  • The error text must not assert a cause. CGEventTapCreate gives no reason.
  • The log line must state the consequence, so a user can connect it to the behavior they see.

Kanata version

kanata v1.12.1-prerelease-1

Debug logs

08:53:42.8542 [INFO] kanata v1.12.1-prerelease-1 starting
08:53:44.9302 [INFO] entering the processing loop
08:53:46.0192 [INFO] keyboard grabbed, entering event processing loop
08:53:46.0306 [INFO] ORIGINPROBE startup displays=1 cursor=Some((0.0, 120.734375)) uid=0 euid=0
08:53:46.0307 [INFO] Installing mouse event tap (mouse_keys=false, movement_key=false, emits_buttons=true)
08:53:46.0357 [ERROR] Failed to create mouse event tap. Ensure kanata has Accessibility or Input Monitoring permission in System Settings > Privacy & Security.

ORIGINPROBE startup is my added probe line, not stock kanata. It shows that the window server answers this process at that instant. Machine boot was 08:53:24, so the failure is 22 seconds after boot.

The same log, started from a logged-in terminal instead:

17:26:48.1859 [INFO] Installing mouse event tap (mouse_keys=false, movement_key=false, emits_buttons=true)
17:26:48.2003 [INFO] Mouse event tap installed and active.

Boot starts that failed: 08:57:53, 09:03:55, 08:53:46. Terminal starts that worked: 11:02:03, 15:00:03, 15:16:30, 16:15:33, 16:16:57, 16:34:01, 17:26:48.

Operating system and I/O mechanism

macOS 26.5.2 (25F84), Apple M1 Pro. Karabiner-DriverKit-VirtualHIDDevice 8.0.0. Kanata runs as a root LaunchDaemon.

Additional context

Scope of what I measured, so you can judge it. The binary is a local build of main merged with #2155 (6ab5322), plus my own log lines. The tap creation code and its failure branch are the same on main at 565d507. My own config has no mouse keys in defsrc, so the extra install condition in #2155 is what installed the tap in my runs. I did not yet boot a stock main build with a qualifying config. I will add that measurement to this issue.

This also means the mouse event tap in #2061 and #2155 is inert after every reboot on the documented LaunchDaemon setup. Field reports against that work will be hard to read until the retry lands.

I am happy to write the retry patch if you want it.