`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

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 0 comments · opened Aug 25, 2026

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_KEY explicitly 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 kevent64 on 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 kevent64 park, but that one is fixed by SHELL=/bin/sh and 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), not U.

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.

View original on GitHub ↗