Summary
In a worktree-isolated session (isolation: "worktree" / EnterWorktree), Claude Code 2.1.272 and 2.1.273 refuse Bash calls that contain no git at all and reference only paths inside the worktree. The 2.1.257 changelog says loops, $VAR reads, "$(…)" and heredocs that never touch git were fixed; the shapes below are still refused on 2.1.272/273.
Across six worktree sessions on 2026-09-15..17 this produced 75 refusals and roughly 115 wasted tool calls (the refusal plus the retry), out of 249 tool errors of all kinds in the same window.
Related: #87959, #84182, #93193.
Environment
- Claude Code 2.1.272 and 2.1.273 (CLI, Linux/WSL2 Ubuntu), sessions started inside a linked worktree under
<repo>/.claude/worktrees/<name>
- Models: Opus 5 / Fable 5.1
- Bash tool; no
git invocation in any of the refused commands below
Reproducers (inside the worktree, no git anywhere in the command)
| Command |
Refusal text (verbatim fragment) |
D=/path/to/worktree/.agent/tasks/x/raw; mkdir -p "$D"; python3 $D/summary.py |
runs python with a program computed at runtime (the variable D) inside a construct too complex to verify, so what it runs cannot be shown not to be git |
S=/path/to/worktree/.agent/scratch/cov.py; python3 /path/to/worktree/.agent/scratch/run.py $S |
runs python with a value computed at runtime (the variable S) (a computed argument goes after the script or --) |
W=/tmp/scratch/wiki; ls "$W/Incidents" |
runs ls with a value computed at runtime (the variable W) … |
for f in a.md b.md; do sed -n 2,14p "$f"; done |
runs sed with a value computed at runtime (the variable f) … |
grep -n 'def redact' x.py; sed -n "$(grep -n 'def redact' x.py | cut -d: -f1),+5p" x.py |
runs sed with a value computed at runtime (command output) where an option may stand … |
mkdir -p out && cat > /path/to/worktree/projects/x/REPORT.md <<'EOF' … EOF |
this command is too complex to verify that it stays inside the worktree |
cd /path/to/worktree && for f in run_etc_backup.sh run_db_backup.sh; do sha256sum "$f"; done |
names git in a form too complex to verify that it stays inside the worktree (there is no git token in the command) |
ls runs | head -1 | xargs -I{} sh -c 'cat runs/{}/stdout.log' |
runs sh from a find -exec or xargs slot … |
Every path is a literal inside the worktree or under /tmp; every variable is assigned a literal in the same command.
Expected
Per the worktrees documentation ("How Claude Code enforces isolation"), the command-shape check should refuse only commands whose git invocation cannot be located. A variable assigned a literal path in the same command, a for over literal words, or a $(grep …) argument to sed -n cannot run git. The wording "names git" is wrong when the command contains no git token.
Actual
Each call is refused before it runs; the model retries with a rewritten shape (typically writing the logic to a file and running python3 /literal/path.py), so every refusal costs at least two tool calls.
Requests
- Resolve same-command literal assignments (
D=/literal; … $D/…) before the shape check, as the 2.1.257 note suggests was intended.
- Do not refuse a heredoc whose redirect target is an absolute path inside the worktree because an earlier
&& exists in the call.
- Never report "names git" when the command contains no
git token.
- Offer a setting or environment variable to downgrade the shape check to a warning for sessions that enforce git isolation by other means (e.g. their own PreToolUse hooks), since the docs state the check cannot be turned off.
I can share the full list of 75 refused commands with the preceding and following tool calls on request.
Summary
In a worktree-isolated session (
isolation: "worktree"/EnterWorktree), Claude Code 2.1.272 and 2.1.273 refuse Bash calls that contain nogitat all and reference only paths inside the worktree. The 2.1.257 changelog says loops,$VARreads,"$(…)"and heredocs that never touch git were fixed; the shapes below are still refused on 2.1.272/273.Across six worktree sessions on 2026-09-15..17 this produced 75 refusals and roughly 115 wasted tool calls (the refusal plus the retry), out of 249 tool errors of all kinds in the same window.
Related: #87959, #84182, #93193.
Environment
<repo>/.claude/worktrees/<name>gitinvocation in any of the refused commands belowReproducers (inside the worktree, no git anywhere in the command)
D=/path/to/worktree/.agent/tasks/x/raw; mkdir -p "$D"; python3 $D/summary.pyruns python with a program computed at runtime (the variable D) inside a construct too complex to verify, so what it runs cannot be shown not to be gitS=/path/to/worktree/.agent/scratch/cov.py; python3 /path/to/worktree/.agent/scratch/run.py $Sruns python with a value computed at runtime (the variable S) (a computed argument goes after the script or --)W=/tmp/scratch/wiki; ls "$W/Incidents"runs ls with a value computed at runtime (the variable W) …for f in a.md b.md; do sed -n 2,14p "$f"; doneruns sed with a value computed at runtime (the variable f) …grep -n 'def redact' x.py; sed -n "$(grep -n 'def redact' x.py | cut -d: -f1),+5p" x.pyruns sed with a value computed at runtime (command output) where an option may stand …mkdir -p out && cat > /path/to/worktree/projects/x/REPORT.md <<'EOF' … EOFthis command is too complex to verify that it stays inside the worktreecd /path/to/worktree && for f in run_etc_backup.sh run_db_backup.sh; do sha256sum "$f"; donenames git in a form too complex to verify that it stays inside the worktree(there is nogittoken in the command)ls runs | head -1 | xargs -I{} sh -c 'cat runs/{}/stdout.log'runs sh from a find -exec or xargs slot …Every path is a literal inside the worktree or under
/tmp; every variable is assigned a literal in the same command.Expected
Per the worktrees documentation ("How Claude Code enforces isolation"), the command-shape check should refuse only commands whose git invocation cannot be located. A variable assigned a literal path in the same command, a
forover literal words, or a$(grep …)argument tosed -ncannot run git. The wording "names git" is wrong when the command contains nogittoken.Actual
Each call is refused before it runs; the model retries with a rewritten shape (typically writing the logic to a file and running
python3 /literal/path.py), so every refusal costs at least two tool calls.Requests
D=/literal; … $D/…) before the shape check, as the 2.1.257 note suggests was intended.&&exists in the call.gittoken.I can share the full list of 75 refused commands with the preceding and following tool calls on request.