Bug Description
Claude Code's shell snapshot mechanism stores the PATH with a literal $PATH reference that is not properly expanded when loaded back. This causes critical system directories (/usr/bin, /bin, /usr/sbin, /sbin) to be missing from the Bash tool's PATH environment.
Environment
- Claude Code version: 2.1.71 (native binary, macOS arm64)
- OS: macOS (Apple Silicon)
- Shell: zsh
- Installation: Native (
~/.local/share/claude/versions/)
Steps to Reproduce
- Install Claude Code natively on macOS
- Start a Claude Code session — a shell snapshot is generated at
~/.claude/shell-snapshots/snapshot-zsh-*.sh
- Run any Bash tool command:
echo $PATH
- Observe the PATH contains a literal
$PATH string instead of expanded system paths
Observed Behavior
The shell snapshot's last line stores:
export PATH=$PATH\:/Users/<user>/.nvm/versions/node/v22.18.0/bin\:/opt/homebrew/bin\:/usr/local/bin
When Claude Code loads this snapshot, $PATH is treated as a literal string rather than being shell-expanded. The resulting PATH in the Bash tool is:
/Users/<user>/.local/bin:$PATH:/Users/<user>/.nvm/versions/node/v22.18.0/bin:/opt/homebrew/bin:/usr/local/bin
Note the literal $PATH in the middle. System directories from /etc/paths (/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin) are never included.
Expected Behavior
The PATH should include macOS system directories (/usr/bin, /bin, etc.) sourced from /etc/paths. Either:
- The snapshot should store the fully-resolved PATH (not a
$PATH reference), or
- The snapshot loader should properly shell-expand
$PATH before setting the environment variable
Impact
git submodule fails because its internal shell scripts call basename, sed, uname (all in /usr/bin):
/opt/homebrew/opt/git/libexec/git-core/git-submodule: line 7: basename: command not found
/opt/homebrew/opt/git/libexec/git-core/git-sh-setup: line 77: sed: command not found
/opt/homebrew/opt/git/libexec/git-core/git-sh-setup: line 292: uname: command not found
- Plugin installation fails for any plugin whose source uses git submodules (e.g.,
figma@claude-plugins-official)
- Direct shell commands like
ls, cat, grep, head, tail are not found (though Claude Code's built-in Read/Grep tools are unaffected)
- This has been present since the first snapshot (v2.1.20, Jan 27 2026) and persists across all 13 snapshots through v2.1.71
Workaround
Add an explicit PATH to ~/.claude/settings.json:
{
"env": {
"PATH": "/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/Users/<user>/.local/bin"
}
}
This is fragile — it requires manual updates when tools like nvm change Node versions.
Root Cause Analysis
The snapshot file (~/.claude/shell-snapshots/snapshot-zsh-*.sh) uses escaped colons (\:) in the PATH export line, suggesting the snapshot generator escapes special characters. However, the $PATH variable reference is preserved as-is. When the snapshot loader processes this line, it appears to unescape \: → : but treats $PATH as a literal string rather than performing variable substitution.
A fresh macOS login shell correctly resolves PATH via /etc/paths → path_helper(8) → .zprofile → .zshrc, but the snapshot's export PATH= overrides the fully-resolved PATH with the broken version.
Bug Description
Claude Code's shell snapshot mechanism stores the PATH with a literal
$PATHreference that is not properly expanded when loaded back. This causes critical system directories (/usr/bin,/bin,/usr/sbin,/sbin) to be missing from the Bash tool's PATH environment.Environment
~/.local/share/claude/versions/)Steps to Reproduce
~/.claude/shell-snapshots/snapshot-zsh-*.shecho $PATH$PATHstring instead of expanded system pathsObserved Behavior
The shell snapshot's last line stores:
When Claude Code loads this snapshot,
$PATHis treated as a literal string rather than being shell-expanded. The resulting PATH in the Bash tool is:Note the literal
$PATHin the middle. System directories from/etc/paths(/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin) are never included.Expected Behavior
The PATH should include macOS system directories (
/usr/bin,/bin, etc.) sourced from/etc/paths. Either:$PATHreference), or$PATHbefore setting the environment variableImpact
git submodulefails because its internal shell scripts callbasename,sed,uname(all in/usr/bin):figma@claude-plugins-official)ls,cat,grep,head,tailare not found (though Claude Code's built-in Read/Grep tools are unaffected)Workaround
Add an explicit PATH to
~/.claude/settings.json:{ "env": { "PATH": "/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/Users/<user>/.local/bin" } }This is fragile — it requires manual updates when tools like nvm change Node versions.
Root Cause Analysis
The snapshot file (
~/.claude/shell-snapshots/snapshot-zsh-*.sh) uses escaped colons (\:) in the PATH export line, suggesting the snapshot generator escapes special characters. However, the$PATHvariable reference is preserved as-is. When the snapshot loader processes this line, it appears to unescape\:→:but treats$PATHas a literal string rather than performing variable substitution.A fresh macOS login shell correctly resolves PATH via
/etc/paths→path_helper(8)→.zprofile→.zshrc, but the snapshot'sexport PATH=overrides the fully-resolved PATH with the broken version.