[BUG] macOS sandbox: every Bash call aborts in sandbox-exec (is_interned, intern.c:63) in a repo with many git worktrees; profile only rebuilt on settings reload

Status Open
Reported on v2.1.296
Maintainer reply None cached
Activity 0 comments · opened Oct 14, 2026

Environment

  • Claude Code 2.1.296 (observed 2026-10-13; the binary has since auto-updated to 2.1.299, not yet re-triggered there)
  • macOS 14.6.1 (23G93), Apple Silicon (Mac15,9), Ghostty terminal
  • sandbox.enabled: true, autoAllowBashIfSandboxed: true, bypassPermissions mode
  • One git repository with 12–16 entries under .git/worktrees/ (several parallel Claude Code sessions, each in its own worktree under .claude/worktrees/ or beside the repo)

Symptom

Every sandboxed Bash tool call, including echo hi, fails instantly with exit 1 and this on stderr:

Assertion failed: (bytes_end <= buffer_end), function is_interned, file intern.c, line 63.

dangerouslyDisableSandbox: true works. Four sessions on the same machine hit it the same afternoon, and one crash report per attempt lands in ~/Library/Logs/DiagnosticReports/sandbox-exec-*.ips (26 of them in about two hours).

Crash report (crashed thread, trimmed)

Process: sandbox-exec, parent: claude, SIGABRT (Abort trap: 6)
libsystem_c.dylib        __assert_rtn
libsandbox.1.dylib       sb_context_intern_string + 212
libsandbox.1.dylib       sb_textbuf_with_string_copy + 40
libsandbox.1.dylib       ___define_filter_block_invoke + 1920
libsandbox.1.dylib       opexe_0 / Eval_Cycle / scheme_load_file / _parse_file
libsandbox.1.dylib       compile + 6156
sandbox-exec             main

Apple's sandbox-exec aborts while compiling the Seatbelt profile Claude Code passes with -p, so the user's command never runs.

What I could establish

  • Not profile size: synthetic profiles with 1,000 literal paths (74 KB) compile fine, and an over-long single string fails cleanly with Error reading string rather than asserting.
  • Content/layout dependent. The dynamic part of the profile is the protected-path list: for every entry of .git/worktrees/, Claude Code adds config.worktree, config.worktree.lock and commondir, and the sandbox runtime folds sibling directories with the same leaves into alternation regexes (chunked at roughly 900 bytes). With 12–16 worktrees that other sessions kept adding and removing, some compositions trip the libsandbox assert.
  • The profile is computed when the sandbox config is (re)built, not per command, so a session stays broken while the worktree set keeps changing. A settings reload fixes it: touching .claude/settings.json made the very next Bash call succeed. Restarting the session also works.
  • A reload can also introduce the crash. Later the same day, in a session whose sandbox had been working for hours, I removed three stale worktrees (14 entries down to 11) and then edited ~/.claude/settings.json; the profile rebuilt by that reload asserted on the very next Bash call. Adding one worktree (12 entries) and touching a settings file rebuilt a profile that compiles. So the trigger is the exact set of worktree names at rebuild time, not their number, and every session in the repo is affected at once because they all reload the same settings file.
  • I could not reduce it to a standalone profile to attach: the failing profile is only visible as the sandbox-exec -p <profile> argv of a process that is reaped immediately, and the .ips reports do not include argv. If there is a debug flag that dumps the generated profile, I can produce a minimal repro.

Repro (best effort, intermittent)

  1. A repo with ~15 git worktrees and several long-lived Claude Code sessions in it.
  2. Add and remove worktrees from other sessions over an hour or two.
  3. In an older session, run any Bash command: at some point every call fails with the assert above until a settings file changes or the session restarts.

Suggestions

  • Treat a sandbox-exec SIGABRT during profile compilation as a Claude Code failure: rebuild the profile against the current filesystem, or fall back to the unsandboxed-command prompt, instead of surfacing the libsandbox assert as the command's own failure.
  • Reduce the per-worktree enumeration (one .git/worktrees/** rule instead of three literals per worktree), or chunk the alternation regexes differently, so the generated profile does not change shape with every worktree another session creates.
  • Dump the generated profile under --debug so this class of issue can be reduced.

Workaround for anyone hitting this

Touch any settings file (.claude/settings.json or settings.local.json) to force a profile rebuild, run git worktree prune, or turn the sandbox off with /sandbox and restart the session.

View original on GitHub ↗