Worktree isolation inflates spawned-shell env past ARG_MAX → E2BIG on all Bash, unrecoverable without restart
Summary
After dispatching a subagent with isolation: 'worktree', every subsequent Bash tool call in the parent session fails with:
E2BIG: argument list too long, posix_spawn '/bin/zsh'
No shell can be spawned for the rest of the session — even echo ok fails identically. Non-shell tools (Read/Edit/Write/Grep) keep working because they don't spawn a shell. There is no in-session recovery; only a full CLI restart clears it.
Environment
- Claude Code v2.1.200
- macOS (Darwin),
ARG_MAX≈ 1 MB - Model: Opus 4.8
- A project with many auto-registered skills (~38 in the repo where this reproduced), each carrying a full description
- Multi-agent worktree workflow (an "EM" parent session dispatches "IC" subagents with
isolation: 'worktree')
Root cause (hypothesis)
Creating the worktree triggers auto-registration of the repo's skills a second time, scoped to the worktree path (e.g. .claude/worktrees/agent-<id>:<skill>), each carrying its full description. These skill descriptions appear to be injected into the spawned shell's environment (envp). The duplicated skill set pushes total envp size over ARG_MAX, so posix_spawn('/bin/zsh') fails before it can run anything.
Evidence:
- Bash worked fine before the worktree was created; the first failure immediately followed the worktree's skill-registration dump (a system message re-listing all skills scoped to the worktree path).
- The failure is on shell spawn (
posix_spawn '/bin/zsh') — i.e. argv/envp size — not on the command;echo okfails the same way. - Non-shell tools are unaffected, consistent with a limit hit only at process-spawn time.
- Follow-on: stale worktrees left on disk from a prior session re-register on the next session's startup, so a fresh session can begin already near
ARG_MAXand see intermittentE2BIGon longer commands before it ever dispatches a worktree agent. (In the repro repo, ~60 worktrees had accumulated across sessions.)
Reproduction
- Open a session in a repo with many skills (~38).
- Dispatch a subagent with
isolation: 'worktree'. - After the worktree registers its scoped skills, run any Bash call in the parent.
→ E2BIG: argument list too long, posix_spawn.
Accumulation variant: leave several worktrees on disk, start a fresh session, and observe intermittent E2BIG on longer commands from the start.
Impact
Total loss of shell-dependent work mid-session, with no in-session recovery:
gh(PR review, issue filing, comments, merge), git, the test suite /tsc/ lint,/code-review,/security-review— anything that spawns a shell.- For a multi-agent (EM/IC) workflow this is severe: the parent can dispatch a worktree agent and read its diff, but then can't run review gates, update GitHub, dispatch the next wave, or merge — the session strands at the review gate and a human has to intervene.
Recovery today (restart only)
- Pruning the worktree on disk (
git worktree remove <path> --force) does not restore Bash — the running process'senvpwas already inflated at spawn time; removing the worktree afterward doesn't shrink the live process's environment. /cleardoes not help (same OS process, sameenvp).- Only a full CLI restart recovers (
claude --continue/claude --resumespawns a fresh process with a clean environment). With the worktree already pruned, the fresh session also won't re-register the scoped skills.
Proposed fixes
- Keep skill metadata out of the spawned shell's environment. Skill descriptions are agent-context data, not shell env; passing them via
envpis what blowsARG_MAX. Move them out of the spawned process's environment entirely. - Don't re-register the full skill set for a worktree of the same repo. A worktree already has the repo's skills; the second, path-scoped registration is what doubles the payload.
- Prune stale worktree skill registrations on session startup so a fresh session doesn't inherit a pre-inflated env from a prior session's leftover worktrees.
- Guard the spawn. Detect
envpapproachingARG_MAXand fail loudly with a clear message + self-recovery hint, instead of an opaqueE2BIGon every shell call. - Allow an in-session env reset — a way to shrink/reset the tool environment without a full restart would turn a session-ending failure into a hiccup.
Workaround
Keeping .claude/worktrees/ pruned (removing worktrees whose branches are merged + clean) relieves the pressure, but it's mitigation, not a fix — the core issue is that skill metadata is being passed through the spawned shell's envp at all, and that a same-repo worktree re-registers its skills.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Your root-cause hypothesis holds up against the mechanics.
posix_spawnhas to fit envp + argv insideARG_MAX(≈1 MB on macOS, ≈2 MB on Linux —getconf ARG_MAX). The tells you listed are exactly what an envp-overflow looks like, not a command-length one:posix_spawn '/bin/zsh'), so it's argv/envp size, not the command — which is whyecho okdies identically;posix_spawn;So a second, worktree-scoped copy of ~38 full skill descriptions landing in
envpis a very plausible way to cross a 1 MB ceiling — the descriptions are the big, duplicated payload.Things that help until the injection is fixed server-side:
Measure how close you already are. Before it bites, compare env size to the limit:
If envp is already a large fraction of
ARG_MAXat rest, worktree isolation (which roughly doubles the skill payload) is what tips it over. Repos with many always-registered skills are the ones at risk.Cut the payload or skip the isolation.
isolation: 'worktree'on the dispatch until this is fixed; the flat (non-worktree) path doesn't re-register the second scoped copy.Honest limits: all of the above are you working around a payload you didn't choose to put there. The durable fix is server-side — skill descriptions shouldn't ride in
envpof spawned shells at all (a file/fd or an in-process channel avoids theARG_MAXceiling entirely), and worktree creation shouldn't register a second full copy scoped to the worktree path. Until then, "keep resting envp well under half ofARG_MAX" is the practical guardrail for anyone running worktree fan-out on a skill-rich repo.Since
posix_spawnhas to fit envp + argv together insideARG_MAX, it's worth ruling out the argv side too while chasing the env-side inflation here — they draw on the same budget, so a large argv makes the env threshold easier to trip (and vice-versa).On the argv side, the inline sandbox profile derives write-deny paths from the registered
projectsin~/.claude.json, and those entries are never removed when their directory is deleted. In a multi-worktree / subagent-isolation workflow that creates and tears down many dirs, they accumulate unbounded. One setup had ~1,250projectsentries, ~92% pointing at deleted dirs (~1 MB of argv on their own) — enough to trip E2BIG with very few live worktrees. Pruning the missing-dir entries restored Bash immediately.A cheap general guard for the argv half: skip any deny-path whose directory no longer exists (one
isdir()filter). Details/measurements: https://github.com/anthropics/claude-code/issues/73437#issuecomment-5095540627Thanks for the detailed repro steps. I ran them on 2.1.233 on Linux: a fresh repo with 40 skills (~4 KB descriptions each), then a worktree-isolated subagent, then re-checked the shell.
env | wc -cwas byte-identical before and after (8523), theCLAUDE*var count didn't change, andecho okkept working. So skill descriptions don't end up in the spawned shell's environment — the env-inflation mechanism in the report isn't what was happening.The E2BIG failure itself was real, though, with a different cause: in sandboxed sessions the spawned command line grew with the number of registered git worktrees (entries in the git worktree registry survive deleting the checkout directories, so heavy worktree-isolation use accumulates them), and part of that list was duplicated. This was fixed in 2.1.203 — "Fixed Bash failing with 'argument list too long' in repos with many git worktrees" (changelog). Since then the duplication is gone, and if a session ever does hit the OS limit, Bash reports exact size accounting and recovery steps (e.g.
git worktree prune) instead of the raw posix_spawn error, so it's no longer unrecoverable.Closing as fixed — if you still hit this on 2.1.203 or later, reply here (ideally with
git worktree list | wc -lfrom the affected repo) and we'll reopen.🤖 Generated with Claude Code