Skip to content

Shell snapshot PATH export contains literal $PATH — system directories lost #31799

@hahnpierre

Description

@hahnpierre

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

  1. Install Claude Code natively on macOS
  2. Start a Claude Code session — a shell snapshot is generated at ~/.claude/shell-snapshots/snapshot-zsh-*.sh
  3. Run any Bash tool command: echo $PATH
  4. 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/pathspath_helper(8).zprofile.zshrc, but the snapshot's export PATH= overrides the fully-resolved PATH with the broken version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:bashbugSomething isn't workinghas reproHas detailed reproduction stepsplatform:macosIssue specifically occurs on macOSstaleIssue is inactive

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions