Contents
Put export PATH="$PATH:~/.local/bin" in your .bashrc and everything appears to work. kek resolves, which agrees, you move on. The environment variable now contains a literal tilde character, though, and that is the thing every child process inherits.
Disconnect3d described the problem and framed it as a quoted tilde not being expanded. I reproduced it, and the Bash result is more interesting than “it doesn’t work”: in Bash it does work, which is exactly why the bug survives.
What I ran
Setup, on Linux with Bash 5.2.21 and dash from Ubuntu. A fake HOME contains an executable kek that prints REAL-home. A working directory contains ./~/.local/bin/kek that prints PLANTED-cwd. Then I set PATH with a literal tilde and ran kek through different launchers:
export PATH="/usr/bin:/bin:~/.local/bin"
kek # run from Bash
env kek
sh -c kek
echo | xargs kek
python3 -c 'import subprocess; subprocess.run(["kek"])'
printf 't:\n\tkek\n' | make -f -
| Launcher | What ran |
|---|---|
kek typed in Bash | REAL-home |
env kek | PLANTED-cwd |
sh -c kek (dash) | PLANTED-cwd |
xargs kek | PLANTED-cwd |
Python subprocess.run(["kek"]) | PLANTED-cwd |
make recipe | PLANTED-cwd |
In every case echo "$PATH" printed ~/.local/bin with the tilde intact. The variable never changed. What differed is who did the lookup.
Why Bash hides it
Assignment is the first place a tilde can be expanded, and the quotes prevent that. Unquoted, export PATH=$PATH:~/.local/bin is expanded at assignment time in both Bash and dash, because the Bash manual says tilde-prefixes are checked after the first = and after any : in an assignment. I confirmed that: the printed PATH contained the absolute home path and everything resolved correctly.
Quoting turns that off, so the value stays ~/.local/bin. A program launching a child normally hands PATH to execvp or an equivalent, which treats each colon-separated entry as a plain directory. ~ is a valid directory name, so the entry means ./~/.local/bin, relative to the current directory. That is what env, dash, xargs, Python and make did above.
Bash is the exception because its own command lookup expands a leading tilde in each PATH element before searching. My run is consistent with that: type kek in Bash reported the absolute home path, while env kek found the planted file. I did not read Bash source for this, so treat the mechanism as the explanation that fits the output, not a cited guarantee. The behavior, on 5.2.21, is what I measured.
One more datapoint: the system which binary on this machine, run under the same PATH, printed ~/.local/bin/kek. That is a relative path resolved from the current directory, so even the tool you use to debug the problem can mislead you in the opposite direction from type.
Why this is a security problem, not only a nuisance
A relative PATH entry means the directory you are standing in influences which binary runs. Anyone who can write ./~/.local/bin/<name> in a directory you work in, such as a cloned repository or an unpacked archive, can have it run when a tool resolves <name> there. The source post does not spell out an attack, so this is my reading of the demo, and I only tested it with files I created myself.
It also produces the more ordinary failure: a script that works from your terminal and fails in cron, a Makefile, a CI step or a Python tool that shells out, with “command not found” and no hint that the interactive shell was covering for a bad PATH.
Finding it
Check the environment, not the shell’s lookup:
echo "$PATH" | tr ':' '\n' | grep -n '~'
Any output is a literal tilde. Relative entries more generally, such as an empty element or ., are the same class of problem. Test with env rather than a bare command, since env does not get Bash’s help:
env sh -c 'command -v kek'
Fix
Use $HOME and keep it quoted:
export PATH="$PATH:$HOME/.local/bin"
$HOME is expanded even inside double quotes, so a space in the path cannot break the assignment, which is the other fragility the original post notes about the unquoted tilde form. Check files other than .bashrc too: .profile, Dockerfile ENV PATH= lines and CI config are all places the same string can be copied into.
I did not test zsh, since it was not installed here. The source post’s demo, which appears to be from macOS judging by its ls output, shows a quoted tilde failing to find the binary. That fits a shell without Bash’s lookup expansion, but the post does not say which shell it used, and I have not checked.
Sources
- disconnect3d: The tilde in your PATH may not be your HOME, October 2, 2026
- GNU Bash manual: Tilde Expansion
- Local reproduction on Bash 5.2.21 and dash (Ubuntu), October 12, 2026