[BUG] `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1` leaves an empty `~/.bash_profile` behind, silently breaking login-shell PATH
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?
With CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1, Claude Code creates empty 0-byte files in $HOME at startup — one for each path in a fixed list of shell and package-manager dotfiles that does not already exist. It never removes them, and deleting one then running again recreates it.
The damaging one is ~/.bash_profile.
Bash login shells read only the first of ~/.bash_profile → ~/.bash_login → ~/.profile. Of those three, a stock Debian/Ubuntu account has only ~/.profile — /etc/skel ships .profile, .bashrc and .bash_logout, and no .bash_profile. So for as long as the empty ~/.bash_profile exists, no login shell reads ~/.profile: ~/.local/bin and ~/bin drop out of PATH, ~/.bashrc is not sourced, and anything else ~/.profile loads (~/.cargo/env, for instance) is skipped. On my machine this also removed claude itself from the login-shell PATH, since the native installer puts it in ~/.local/bin.
This is distinct from the project-directory pollution of #17087 and #51405. This is $HOME, so the symptom is not a dirty git status but a quietly altered environment for every login shell on the account.
No sandbox opt-in is involved. Per the sandboxing docs, sandbox.credentials "affects sandboxed Bash commands only", while CLAUDE_CODE_SUBPROCESS_ENV_SCRUB strips credentials "from all subprocesses regardless of sandboxing". I never set sandbox.enabled anywhere.
What Should Happen?
Claude Code should not create the user's shell and toolchain dotfiles.
~/.bash_profile above all. It is not clutter but semantics: its mere existence stops every login shell on the account from reading ~/.profile. It should never be created. The other ten files, and the .pip and .config/pip directories, are empty and inert, but they should not be left behind either.
Removing them at exit would not be enough. I checked while a run was still in flight: ~/.bash_profile is already on disk, and a login shell started at that moment has already lost ~/.profile. An interactive session lasts hours, and a crash or kill -9 would leave the file behind for good.
Error Messages/Logs
None. Nothing is printed, prompted, or logged when the files are created.
Steps to Reproduce
Deterministic, and no authentication is needed: the run fails at the API-key check after the files are written. bubblewrap is installed on my machine; I did not test a host without it.
env -i clears PATH, so resolve the binary first and call it by absolute path.
CLAUDE=$(readlink -f "$(command -v claude)") # e.g. ~/.local/share/claude/versions/2.1.205
# 1. A home dir shaped like a stock Debian/Ubuntu account: ~/.profile, no ~/.bash_profile
rm -rf /tmp/fakehome && mkdir -p /tmp/fakehome/proj /tmp/fakehome/.local/bin
printf 'export PATH="$HOME/.local/bin:$PATH"\n' > /tmp/fakehome/.profile
# 2. BEFORE — the login shell reads ~/.profile
env -i HOME=/tmp/fakehome bash -lc 'echo "$PATH"' # contains ~/.local/bin
# 3. Run Claude Code once with the variable set
cd /tmp/fakehome/proj
env -i HOME=/tmp/fakehome PATH=/usr/bin:/bin ANTHROPIC_API_KEY=sk-invalid \
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 \
"$CLAUDE" -p 'hi' --max-turns 1 </dev/null
# 4. A file that did not exist now does; the one that did exist is untouched
wc -c /tmp/fakehome/.bash_profile # 0
wc -c /tmp/fakehome/.profile # unchanged
# 5. AFTER — the login shell no longer reads ~/.profile
env -i HOME=/tmp/fakehome bash -lc 'echo "$PATH"' # ~/.local/bin is gone
# 6. Removing the stub restores it
rm /tmp/fakehome/.bash_profile
env -i HOME=/tmp/fakehome bash -lc 'echo "$PATH"' # back to normal
Running the same block without CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 adds only .claude and .claude.json, and steps 2 and 5 print the same PATH.
On a home directory where none of them exist yet, the run creates these 11 files — all 0 bytes — and 2 directories:
.bash_aliases .bash_profile .bashrc .bunfig.toml .gitconfig
.netrc .npmrc .profile .yarnrc .yarnrc.yml
.zshrc .config/ .pip/
~/.profile is on that list, which is why step 1 seeds it: because it already exists, it is left alone, and wc -c shows its byte count unchanged. Existing dotfiles keep their contents and their mtimes. Only the files you did not have get created — which is why this goes unnoticed on a real machine.
Claude Model
Not sure / Multiple models
Is this a regression?
No, this never worked
Last Working Version
None
Claude Code Version
2.1.205
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other
Additional Information
Claude Code names these files itself. Run it with the variable set inside a git repository, and it appends this to .git/info/exclude:
# claude-code scrub-mode stubs
/bunfig.toml
/package.json
/.npmrc
/.yarnrc
...
So for the project directory the stubs are deliberately kept out of git status — the side effect is recognised and handled there. Nothing equivalent happens for $HOME, where the same mechanism drops ~/.bash_profile.
Two more things from that same file suggest nothing tracks what was created: the block is appended again on every run (the .git/info/exclude in my repo now contains it twice), and the stubs in $HOME are never removed.
Versions. I ran the reproducer against every build I still have installed — 2.1.198, 2.1.199, 2.1.201, 2.1.202, 2.1.204 and 2.1.205 — and all six create ~/.bash_profile.
Environment details. Native installer, ~/.local/bin/claude → ~/.local/share/claude/versions/. Kernel 6.8.0-117-generic. Terminal is Ghostty running tmux, shell is bash. bubblewrap 0.9.0-1ubuntu0.1 (behaviour on a host without it: not tested). sandbox was never enabled in any settings file. None of this matters to the bug — the reproducer above is non-interactive and writes the files before any terminal state is involved.
Workaround. Make ~/.bash_profile source ~/.profile. I verified this survives: after another Claude Code run the file's contents are unchanged, so the line stays.
if [ -f "$HOME/.profile" ]; then . "$HOME/.profile"; fi
Related.
- #46584 — same symptom, no maintainer response, auto-closed as stale. #46585 — its duplicate, closed and locked.
- #74799 — open, no response yet: stub files still created on startup (Fedora)
- #17087 — the project-directory variant, closed as completed by a maintainer
- #51405 — same area, closed by its own author