Headless `claude -p` wedges at bootstrap when spawned by a macOS LaunchAgent — regression in 2.1.203, persists in 2.1.204 (2.1.202 unaffected)

Open 💬 0 comments Opened Jul 9, 2026 by indianajensen

Summary

Since v2.1.203, ANY claude -p invocation whose process ancestry is a macOS LaunchAgent (gui domain) hangs indefinitely at bootstrap: no transcript file is created under ~/.claude/projects/, zero network sockets are opened, no stderr is produced, and the process sleeps on its main thread until killed. The identical commands run from an interactive shell — including fully detached (setsid-equivalent, stdin /dev/null) and with a minimal environment — complete normally in seconds. v2.1.202 runs the same LaunchAgent workloads green.

Environment

  • macOS (Darwin 25.5.0, Apple Silicon)
  • Claude Code 2.1.203 and 2.1.204 (native installer; versions in ~/.local/share/claude/versions/)
  • LaunchAgent in the gui domain (launchctl print gui/$UID/<label> shows it loaded), WorkingDirectory set, PATH provided via the wrapper script

Timeline evidence

A production LaunchAgent pipeline (3 scheduled headless runs/day) ran green for days on 2.1.202. The CLI auto-updated to 2.1.203 at 22:14 on 7 Jul; the very next scheduled run (07:00, 8 Jul) wedged and every launchd-spawned run since has wedged identically (2.1.204 auto-installed mid-window; same behaviour). Interactive sessions on the same machine, same versions, work throughout.

Bisect (all under REAL launchd via launchctl kickstart, 120s timeout each, v2.1.204)

| Probe | Invocation | Result |
|---|---|---|
| A | claude -p "Reply with exactly: PROBE_A_OK" --bare (ANTHROPIC_API_KEY in env) | WEDGED |
| B | A + --strict-mcp-config --mcp-config <empty mcpServers json> | WEDGED |
| C | B + --permission-mode bypassPermissions --disallowedTools "<long list>" | WEDGED |
| D | C with a ~15KB prompt | WEDGED |
| E | claude -p "..." with OAuth/keychain auth (no --bare, no API key) | WEDGED |

So: auth mode (API key vs OAuth), --bare, MCP config, permission mode, tool deny-lists, and prompt size are ALL eliminated. The launchd ancestry itself is the trigger.

Also eliminated interactively (all COMPLETE from a shell on 2.1.204): detached + no TTY + stdin /dev/null; minimal env (HOME/PATH/USER/TMPDIR only); the exact production flag sets, sandbox settings included.

Observed state of a wedged process

  • sample <pid>: main thread parked in the CLI binary (dyld start → main), no interesting activity
  • lsof: ~13 fds, no TCP/UDP sockets at all
  • No transcript/session file ever created under ~/.claude/projects/<cwd-slug>/
  • No child processes (rules out a hung hook — and --bare skips hooks anyway)
  • Killable with plain SIGTERM

Repro

  1. Install 2.1.203 or 2.1.204.
  2. Create a minimal gui LaunchAgent whose program is a bash wrapper that runs claude -p "Reply OK" --bare with ANTHROPIC_API_KEY set, stdout/stderr to a log file.
  3. launchctl bootstrap gui/$UID <plist> then launchctl kickstart gui/$UID/<label>.
  4. The process hangs indefinitely; the same wrapper run from a terminal completes in seconds.
  5. Repoint the CLI launcher symlink at 2.1.202 and kickstart again: completes normally.

Impact

Any unattended/scheduled Claude Code automation on macOS (LaunchAgent-driven agents, cron-adjacent schedulers) is fully broken on ≥2.1.203, and silently: no error, no log line, just a parked process. Given auto-updates land silently, production automations break overnight with no code change — ours cost two days of a production pipeline before the version delta was identified.

Happy to provide the full launchd plists, wrapper scripts, and sample output.

View original on GitHub ↗