`claude -p` deadlocks at startup under launchd on 2.1.243 (2.1.241/2.1.240 fine) — stalls at `[STARTUP] Loading commands and agents`, 0% CPU, SIGTERM-immune; `SHELL=/bin/sh` (#78903) does not help
Summary
Starting with 2.1.243, claude -p deadlocks at startup when launched from a macOS launchd LaunchAgent. It writes no session transcript, burns essentially no CPU, and never recovers. 2.1.241 and 2.1.240 succeed in ~10s under the identical launchd job, so this is a clean regression introduced in 2.1.243.
The last line in --debug before the process goes silent forever is always [STARTUP] Loading commands and agents....
This is not the SHELL=/bin/sh shell-snapshot hang (#78903) — I tested that workaround directly and it does not help on 2.1.243 (details below).
Environment
- macOS 26.5.2, Darwin 25.5.0, Apple Silicon
- Claude Code 2.1.243 (native installer,
~/.local/share/claude/versions/) - Subscription auth (
ANTHROPIC_API_KEYexplicitly unset), Keychain unlocked, machine never sleeps - Invocation:
claude -p "<prompt>" --model sonnet --permission-mode bypassPermissions --strict-mcp-config --allowedTools Read </dev/null, from a LaunchAgent - 10 plugins enabled (29 hooks), ~8 claude.ai remote MCP servers configured
Version A/B — the core evidence
Same LaunchAgent, same prompt, same flags, back-to-back in one run, only the binary path changed:
2.1.243 rc=126 deadlocked (3 centiseconds of CPU across 30s)
2.1.241 rc=0 OK in 10s
2.1.240 rc=0 OK in 10s
Reproduces 100% of the time on 2.1.243, across many runs over several hours.
Where it stops
--debug captured from a deadlocked launchd run. Final lines:
13:14:15.413Z [DEBUG] [STARTUP] Loading commands and agents...
13:14:16.098Z [DEBUG] File /Users/…/.claude.json written atomically
<- nothing further, ever
A healthy run continues immediately:
28.522Z [DEBUG] [STARTUP] Loading commands and agents...
28.534Z [DEBUG] [STARTUP] Commands and agents loaded in 12ms
28.537Z [DEBUG] Passes: Cache stale, returning cached data and refreshing in background
28.538Z [DEBUG] [STARTUP] MCP configs resolved in 62ms (awaited at +122ms)
28.595Z [DEBUG] MCP server "notes": Starting connection with timeout of 30000ms
So Loading commands and agents never returns. This is before MCP setup, which is consistent with --strict-mcp-config making no difference.
State of the hung process
Captured from three live specimens held for 21–44 minutes:
- Main thread parked in
kevent64on an empty kqueue; every Bun worker thread on__ulock_wait2. The event loop has nothing pending — this looks like a lost wakeup, not slow work. - 0% CPU — 1.52s of CPU total across 21 minutes; 0.02s per 20s once stalled. (A healthy run uses 0.59s of CPU in its first 2 seconds.)
- No session transcript is ever created under
~/.claude/projects/<slug>/, so the run produces literally nothing. - No child processes. No network sockets. The only thing keeping the process alive is its own listening
/tmp/cc-socks/<pid>.sock. - Ignores SIGTERM; requires SIGKILL.
What I ruled out (each tested under launchd on 2.1.243, not assumed)
| Tried | Result |
|---|---|
| SHELL=/bin/sh (the #78903 workaround), and /bin/zsh explicitly | still deadlocks |
| --strict-mcp-config | still deadlocks |
| --settings '{"remoteControl":false}' / '{"remoteControlAtStartup":false}' | still deadlocks |
| --settings '{"enabledPlugins":{}}' | still deadlocks |
| --disable-slash-commands | still deadlocks |
| --settings '{"lspServers":{}}' | still deadlocks |
| CLAUDE_CODE_AUTO_CONNECT_IDE=0 | still deadlocks |
| CLAUDE_CODE_DISABLE_HOOK_FORWARDING=1 | still deadlocks |
| CLAUDE_CODE_DISABLE_PLUGIN_FORWARDING=1 | still deadlocks |
| CLAUDE_CODE_SANDBOXED=1, CLAUDE_CODE_FORCE_SANDBOX=0 | still deadlocks |
| CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 | still deadlocks |
| allocating a pty via script -q /dev/null | still deadlocks |
| varying --allowedTools (Read vs Read,Bash) and prompt complexity | still deadlocks |
| --bare | no deadlock — returns in ~10s (then fails Not logged in · Please run /login, since --bare skips plugin credentials) |
--bare is documented as skipping hooks, LSP and plugin credentials. Disabling each of those individually does not avoid the hang, so the trigger appears to be somewhere in that combined path rather than any one of them.
Not environmental
A fresh claude -p from an ordinary shell process succeeded in <45s while two launchd specimens sat hung. I also reproduced the launchd environment exactly — env -i with the LaunchAgent's precise 13 variables (HOME, LOGNAME, PATH, PWD, SHELL, SHLVL, SSH_AUTH_SOCK, TMPDIR, USER, XPC_FLAGS, XPC_SERVICE_NAME, OSLogRateLimit, _) — and it succeeded. So the environment is not the discriminator; launchd's process context is.
Relationship to existing issues
- #78903 — same symptom and same
kevent64park, but that one is fixed bySHELL=/bin/shand has been green since July on 2.1.20x. That workaround does not help here, and this reproduces only on 2.1.243. Different bug or a new regression on top. - #74617 — launchd startup hang attributed to the daemon-service shutdown around 2.1.19x. This machine works fine on 2.1.241, so that does not explain it.
- #69173 — same shape, but closed as a duplicate of #61416 (an exit-side bug) and its symptom differs: it burns CPU and ends with
API Error: The operation timed out. This one is 0% CPU and never ends. - #89272 — headless deadlock in uninterruptible
openat. These specimens are in interruptible sleep (S), notU.
Impact
Every scheduled claude -p automation on this machine is broken on 2.1.243 — 8 LaunchAgent jobs. Because StartCalendarInterval skips a firing while the previous run still holds the slot, an unbounded hang silently cancels every subsequent day's run. Downgrading to 2.1.241 is the only mitigation I found.
Ask
Root-cause the Loading commands and agents step under launchd on 2.1.243, and/or identify which part of what --bare skips is responsible so there is a supported knob for headless invocations.
Happy to supply full --debug output, sample call graphs from the hung processes, and lsof dumps.