[BUG] Bash tool's shared shell cwd leaks silently across calls (parallel and long-distance) — can relocate or corrupt later commands with no reminder
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Human-hand-verified report generated using Claude Code (Sonnet 5). There is another report which includes symptoms of this issue as the third of three items, violating your "this is a single bug report" policy. Essentially this is documented behaviour but it has highly undesirable effects at scale (parallel) and distance, and the agent should at least be made more aware of those risks when using the Bash tool.
The Bash tool's persistent shared shell does track cd across separate tool calls exactly as documented ("The working directory persists between commands") — but nothing ever reminds the model that it does. So any earlier cd — from a parallel sibling call in the same turn, or from an unrelated call many turns earlier — can still be silently in effect when a later command assumes it's running somewhere else (the project root, or wherever the last cd the model remembers issuing was).
Concretely:
- Parallel siblings: two Bash tool calls sent in the same assistant turn share one shell. If call 1 is
cd A && <cmd>and call 2 is a bare<cmd>(nocd), call 2 runs insideAinstead of wherever it was actually meant to run, with no error and no notice — assumingAis a valid path for whatever call 2 runs, its output looks completely normal. - Long-distance persistence: the same leak isn't scoped to "the other call in this batch" — it survives indefinitely, across arbitrarily many intervening tool calls that don't touch cwd, until something explicitly
cds elsewhere. In our reproduction this eventually caused a shell to delete its own working directory: a cleanup command (rmdiron a list of directories, including the one an early forgottencdhad left the shell sitting inside) ran from inside the very directory it was deleting, and the next command failed withgetcwd: cannot access parent directories: No such file or directory.
There is a working safety net, but it only covers one narrow case: if a cd lands outside the session's allowed directories (project root + any additionalDirectories), the tool resets the shell back and reports Shell cwd was reset to <dir> in the result. That path works correctly — we confirmed it fires reliably, is unrelated to hooks or sandbox mode, and isn't a local customization (see Part 1). But it does nothing for cd targets that are in-scope, which is the common case (moving between worktrees, subdirectories, sibling checkouts all under the same parent directory) and exactly where this bug lives.
What Should Happen?
Every Bash tool result should state the shell's actual resulting working directory, not just on the reset path. If that's too noisy for every call, the tool description's parallel-call guidance should say plainly that calls in the same turn are not isolated from each other and that cd persists silently (in-scope) across unrelated later calls too — the current wording ("The working directory persists between commands, but shell state does not") reads as reassuring rather than as the warning it needs to be. Isolating each call's shell/cwd, at least across a single turn's parallel calls, would remove the sharpest edge (case 1) without needing a notice on every call.
Error Messages/Logs
$ pwd
/path/to/project/dir-a
pwd: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
# The first line is the stale, already-deleted logical path — printed
# *before* the error, so it reads as a normal answer if the error line
# is overlooked.) Recovered only by issuing an explicit absolute
# `cd <valid-path>` in a subsequent call.
Steps to Reproduce
_HUMAN NOTE ON BUG REPRO:_ You're not running the commands below, you're _asking Claude Code to run them using its Bash tool_. The simplest thing is probably to ask it to execute the reproduction itself and paste this entire section in.
Two directories must be inside the session's allowed scope (project root or an additionalDirectories entry) — using /tmp or another out-of-scope path instead hits the (working) reset guard and won't reproduce this.
Case 1 — parallel siblings:
mkdir -p <project>/dir-a- In one assistant turn, send two Bash tool calls together: call 1
cd <project>/dir-a && echo "call1" && pwd; call 2echo "call2" && pwd. - Expected: call 2 prints the directory it started in (unchanged since it never
cd'd). Actual: call 2 prints<project>/dir-a.
Case 2 — long-distance / self-destructive:
- Reproduce case 1 so the shell is left inside
<project>/dir-a. - Issue several unrelated Bash calls that don't touch cwd (to establish the leak isn't just "the very next call").
- Issue a cleanup call from a fresh assumption of "we're at the project root":
rmdir <project>/dir-a; mkdir -p <project>/dir-b; pwd. - Expected: runs against the project root. Actual:
rmdirdeletes the directory the shell is still secretly inside; the followingpwd(in the same or a later call) fails withgetcwd: cannot access parent directories: No such file or directory.
Variant: if step 3 uses relative paths (rmdir dir-a; mkdir -p dir-b; pwd), the failure is quieter — rmdir dir-a fails "No such file or directory" because it resolves to dir-a/dir-a (the directory being denied does exist, one level up, which is thoroughly misleading), and mkdir -p dir-b silently creates dir-a/dir-b: an artefact in the wrong place with no error at all.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.220 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Xterm
Additional Information
Related, but distinct — the opposite symptom (shared cwd failing to persist, or resetting silently, which breaks the documented contract in the other direction and affects PostToolUse hooks): #76708, #74741, #79891. Those and this report both point at the same underlying gap: the Bash tool's session-level cwd tracking doesn't reliably reflect what the shell process actually did, in either direction.
Also filed, weaker/bundled evidence for the same "parallel siblings race on shared cwd" mechanism as case 1 above, as the third of three unrelated symptoms in one report: #75865 ("Session repeatedly reports 'logged out' ..."), its "Issue 3" section.
We hit the real-world version of case 1 twice in normal use before reducing it to a minimal repro: a git push intended for one working tree silently ran in a sibling working tree instead (and printed "Everything up-to-date" — indistinguishable from a genuine no-op, so the mis-push wasn't noticed until later), and separately a gh pr comment call with no explicit -R <repo> flag, sent alongside a cd-bearing sibling call, posted to the wrong repository's pull request (caught and deleted within seconds).
---
Disclosure: this report was researched and drafted with Claude Code (Sonnet 5, with the reproduction re-verified in a later Fable 5 session) — reproduction, duplicate search, and drafting were AI-assisted, but the reproduction above was personally re-run and verified by the human submitter before posting, who wrote the final prose and owns follow-up questions.