Bash tool: shell snapshot is generated by a login shell, stripping inherited PATH on Windows + Git Bash
TL;DR
On Windows + Git Bash, Claude Code's shell snapshot generator runs in login mode. Git Bash's /etc/profile rewrites PATH from scratch on login, so the snapshot captures a PATH where the parent shell's activator prepends — Python venv Scripts/, nvm bin/, conda Scripts/, asdf shims, direnv exports — have already been stripped. Every subsequent Bash tool call sources that snapshot and inherits the broken PATH. Environment variables that /etc/profile doesn't touch (VIRTUAL_ENV, NVM_BIN, CONDA_PREFIX, …) survive, producing a half-activated state that no real activator ever produces.
Reproduction
- Windows + Git Bash + Python venv (or nvm / conda / asdf / direnv — any PATH-mutating activator).
- Activate in your terminal:
source .venv/Scripts/activate. Confirmwhich justresolves to the venv. - Launch
claudefrom the same terminal. - In a Bash tool call:
echo $VIRTUAL_ENV→ returns the venv path (correctly inherited)which just→ "not found", despite.venv/Scripts/just.exeexisting on diskecho $PATH→ no.venv/Scripts/, plus duplicate entries (the/etc/profilesignature)
Evidence
~/.claude/shell-snapshots/snapshot-bash-<id>.sh, final line:
export PATH='/c/Users/<USER>/bin:/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/mingw64/bin:/usr/bin:/c/Users/<USER>/bin:[...]'
The duplication of /mingw64/bin, /usr/bin, and ~/bin in the first eight entries is the diagnostic. Git Bash's /etc/profile unconditionally prepends /usr/local/bin:/usr/bin:/bin:/mingw64/bin. The inherited PATH from the parent already contained /mingw64/bin and ~/bin, so they appear twice. That duplication only occurs if the snapshot generator was invoked as a login shell.
(The snapshot itself is well-formed — PATH is fully expanded, no ${PATH} literal, no escaped braces. The generator's output serialization is correct. The bug is in what shell mode the generator was invoked in.)
Why this is not a Git Bash bug
Every other tool that spawns subshells on the same Git Bash inherits PATH cleanly:
- VS Code's integrated terminal
bash -c '...'invoked from any parent Git Bash session- Windows Terminal split panes
- IntelliJ / PyCharm run configurations
- Plain
shin a Git Bash session
None of them lose the venv's Scripts/ from PATH, because none of them invoke login shells for "run a command on behalf of the user." Claude Code's Bash tool is the outlier among tools, not Git Bash among shells. /etc/profile's PATH-reset is doing what login shells are documented to do; the design decision under Claude Code's control is whether to invoke the snapshot generator in login mode at all.
Fix
Spawn the snapshot generator (and the per-call Bash tool subshell that sources the snapshot) as an interactive non-login shell rather than a login shell.
- Loads
~/.bashrc/~/.zshrc— so user aliases, functions, and asdf/nvm/pyenv/direnv init lines still work (the behavior macOS users currently rely on). - Skips
/etc/profile,~/.bash_profile,~/.zprofile,/etc/zprofile— so Git Bash's PATH-reset doesn't fire, and macOS'spath_helperdoesn't redundantly re-derive PATH that was already inherited. - Resolves all PATH-mutating activators in one change: venv, nvm, conda, asdf/mise, rvm/rbenv, direnv, pyenv.
The interactive matters: a non-interactive shell skips .bashrc (per the standard case $- in *i*) ;; *) return;; esac guard most distros ship), which is the proximate cause of the WSL-flavored variant (#36285).
Related issues
- #15128 — same symptom on Windows + Git Bash; closed Not Planned, locked.
- #36285 — same generator, different serialization bug, with a venv on WSL; closed Not Planned.
- #52521 — observation that the Bash tool spawns the user's login shell (raised on macOS, where the behavior is incidentally useful).
- #50181 — sibling snapshot serialization bug (single-quoted
${PATH}); fixed.
References
- GNU Bash startup files: https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html
- Python venv activation: https://docs.python.org/3/library/venv.html
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗