[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
Workaround ✓ Mentioned in description ↑
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,bypassPermissionsmode- 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 stringrather than asserting. - Content/layout dependent. The dynamic part of the profile is the protected-path list: for every entry of
.git/worktrees/, Claude Code addsconfig.worktree,config.worktree.lockandcommondir, 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.jsonmade 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.ipsreports 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)
- A repo with ~15
git worktrees and several long-lived Claude Code sessions in it. - Add and remove worktrees from other sessions over an hour or two.
- 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-execSIGABRT 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
--debugso 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.