2026-07-01 HM Bump Breaks Launchd via User-Domain Default
Context: Watching today’s update run in a tmux pane, I noticed two Failed to start agent … user/501 … I/O error (code 5) lines for atuin-daemon and tldr-update, plus hm switch exiting 1. Chased it down.
Root cause (real one, after two wrong turns)
Today’s flake update bumped home-manager (5d72a29 → 74b38179). That release:
- Moved background-service launchd agents to
domain = lib.mkDefault "user"inside the upstream program/service modules themselves —programs.atuin→ atuin-daemon (modules/programs/atuin.nix:301),services.tldr-update→ tldr-update (modules/services/tldr-update.nix:65), plus a wide sweep (ollama, syncthing, gpg-agent, ssh-agent, git-sync…). So agents I never declare got flipped touser. - On macOS 26, bootstrapping a LaunchAgent into
user/$UIDfails with EIO code 5 (guiworks). This was already in my memory note, but I’d assumed the default stayedgui. - The new HM hard-exits activation on any launchd failure (
activate:if [[ "$launchdStatus" -ne 0 ]]; then exit). So one EIO fails the wholehm switchand skips everyentryAfter [ "setupLaunchAgents" ]block.
Only atuin-daemon/tldr-update broke because everything I declare (litellm, obsidian, macmon) defaults to gui.
The fix
Override launchd.agents.<name>.domain = "gui" (plain assignment beats upstream’s mkDefault) in atuin.nix and tealdeer.nix. One-time transition: launchctl bootout gui/$UID/<label> first (atuin respawns via KeepAlive, but the switch still succeeds once its plist matches). Verified: hm switch exits 0, both agents load in gui, no Background key, second switch idempotent. Committed 1c39c6a (bundled with nix/flake.lock, since the domain option only exists in the bumped HM). Root inputs committed separately (3c76cd2).
Wrong turns (worth remembering)
- First diagnosis: “atuin/tldr lack the restart workaround litellm/obsidian have.” I built a shared
nix/lib/launchd-gui-restart.nixhelper and refactored all four modules onto it (the user had picked “extract shared helper + fix all 4”). Thenhm switchstill failed — becauseentryAfter setupLaunchAgentsnever runs under the new hard-exit. Reverted the entire refactor. - Second diagnosis: “revert/pin home-manager.” The user pushed back (“that’s not it, go deeper”), correctly — reverting dodges the real cause. Going deeper found the per-agent
domain = userin the upstream modules. - Lesson: when a workaround “should work” but doesn’t, trace the actual generated
activatescript (bash -x) rather than trusting the module comment. Theexitat activate:1536 was invisible until I traced it.
Latent risk (flagged, not fixed)
Because of the hard-exit, litellm/obsidian’s entryAfter setupLaunchAgents restart workarounds are now dead too. Their next plist change (version bump) could fail hm switch. General fix is an entryBefore setupLaunchAgents pre-bootout, or wait for nix-community/home-manager#9585.
Addendum: latent risk fixed generically (67cbd23)
Followed up and fixed the reload bug for all agents, not just the domain issue. Root of the reload failure: HM reloads a changed agent with launchctl bootout --wait (unsupported on macOS → old agent never unloaded → bootstrap EIO), then hard-exits, so the per-module entryAfter setupLaunchAgents restart blocks (litellm/obsidian) never ran.
New modules/launchd-reload-fix.nix: one entryBefore setupLaunchAgents activation that diffs each incoming plist ($newGenPath/LaunchAgents/<label>.plist, a global the harness exports) against the deployed copy and boots out (gui domain) any that changed, so HM’s own bootstrap lands in a free slot. Auto-discovered via home.nix’s listFilesRecursive. Deleted the now-redundant restart blocks from litellm and obsidian (net −21 lines). Verified end-to-end: appended a drift marker to a loaded agent’s deployed plist → hm switch reloaded it cleanly (exit 0, no EIO, plist restored); unchanged switch does no reload churn. Known gap: agent removal still hits the same --wait bug in HM’s removeAgent (rare here). Drop once home-manager#9585 lands.
Related
2026-07-01 Flake Debug Warnings and Stale-Close Tag Cleanup · 2026-06-08 Nix Home-Manager Review and Cross-Platform Fixes