Linux Error Guide: 'command not found' PATH and Missing Packages
Fix the 'bash: command not found' error in Linux: diagnose missing packages, broken PATH, non-login shells, stale hash cache, sudo secure_path, and bad shebangs.
- #linux
- #troubleshooting
- #errors
- #shell
Overview
A “command not found” error means the shell looked through every directory in your PATH for the named executable and found nothing. The command is either not installed, lives in a directory that is not on PATH, has a typo, or is reachable by a normal shell but stripped away under sudo. The shell never even tried to run a program — it failed at the lookup stage.
You will see it printed to standard error the moment you press Enter:
bash: terraform: command not found
The same condition surfaces in scripts, cron jobs, and CI runners, where it is more confusing because an interactive shell on the same host runs the command fine. That difference is almost always a PATH that is built differently for login, non-login, and non-interactive shells.
Symptoms
- The shell prints
bash: <cmd>: command not foundand returns exit code127. - The command works for one user, shell, or login type but not another.
- It works interactively but fails inside a script, cron, or systemd unit.
- It works for your user but fails under
sudo(“sudo:: command not found”).
kubectl version --client
echo "exit code: $?"
bash: kubectl: command not found
exit code: 127
Common Root Causes
1. The package providing the command is not installed
The binary simply does not exist on this host. command -v finds nothing, and the package manager can tell you which package would provide it.
command -v htop || echo "not on PATH"
# Debian/Ubuntu: search the package that ships the file
apt-file search bin/htop
# RHEL/Fedora: which package provides it
dnf provides '*/bin/htop'
not on PATH
htop: /usr/bin/htop
htop-3.2.2-2.fc40.x86_64 : Interactive process viewer
Repo : fedora
Matched from:
Filename : /usr/bin/htop
Install the named package (apt install htop or dnf install htop) and the command appears.
2. The binary exists but is not on PATH
The executable is installed, but the directory holding it is not in PATH, so the shell never looks there.
ls -l /opt/nodejs/bin/node
echo "$PATH"
-rwxr-xr-x 1 root root 89234104 May 12 09:31 /opt/nodejs/bin/node
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
The binary is at /opt/nodejs/bin, which is absent from PATH. Add it (e.g. export PATH="/opt/nodejs/bin:$PATH") or call it by full path.
3. PATH was clobbered in a shell rc / login vs non-login shell
A line like PATH=/usr/bin (an assignment, not a prepend) in ~/.bashrc or ~/.profile overwrites the inherited value. Login, non-login, and non-interactive shells source different files, so the breakage may appear only in one context.
grep -nE 'PATH=' ~/.bashrc ~/.profile ~/.bash_profile 2>/dev/null
# compare what a non-interactive shell actually sees
bash -c 'echo "$PATH"'
/home/deploy/.bashrc:42:PATH=/usr/bin:/bin
/usr/bin:/bin
Line 42 replaces PATH instead of extending it. Change it to PATH="/usr/local/bin:$PATH" so the existing directories survive.
4. The executable lives in a non-standard directory
Tools installed via pip --user, language version managers, or make install land in places like ~/.local/bin or /usr/local/bin that are not always on PATH for every shell.
ls -l ~/.local/bin/aws /usr/local/bin/composer 2>/dev/null
command -v aws
-rwxr-xr-x 1 deploy deploy 1132 Jun 01 14:08 /home/deploy/.local/bin/aws
aws exists in ~/.local/bin but command -v aws prints nothing because that directory is missing from PATH. Add ~/.local/bin to PATH in your shell rc.
5. Typo or wrong binary name
The command is real but spelled wrong, or you are reaching for a name the tool does not actually install (for example docker-compose vs docker compose).
type pyhton
command -v python3
bash: type: pyhton: not found
/usr/bin/python3
pyhton is a transposition of python; the correctly named binary is present. Verify the exact name with type/command -v before assuming the tool is missing.
6. Stale hash cache or broken symlink
Bash caches the resolved path of commands in a hash table. If a binary moves (e.g. an upgrade relinks it), the cache can point at a path that no longer exists. A broken symlink produces the same symptom.
hash -t terraform
ls -l "$(command -v terraform)"
/usr/local/bin/terraform
lrwxrwxrwx 1 root root 41 Mar 02 11:20 /usr/local/bin/terraform -> /opt/terraform/1.5.7/bin/terraform
If that target is gone, the symlink is broken (ls shows it in red / reports “No such file”). Clear the cache with hash -r and repair or recreate the symlink.
Diagnostic Workflow
Step 1: Confirm what the shell resolves
type <cmd>
command -v <cmd>
which <cmd>
If all three find nothing, the command is not reachable on the current PATH. If type reports a path that does not exist, suspect a stale hash or broken symlink.
Step 2: Check whether the binary exists anywhere
# common install locations
ls -l /usr/bin/<cmd> /usr/local/bin/<cmd> ~/.local/bin/<cmd> /opt/*/bin/<cmd> 2>/dev/null
# or search the whole tree
find / -name '<cmd>' -type f 2>/dev/null
A hit here means the problem is PATH, not a missing package. No hit means you need to install it.
Step 3: Inspect the current PATH and where it came from
echo "$PATH" | tr ':' '\n'
grep -nE 'PATH=' ~/.bashrc ~/.bash_profile ~/.profile /etc/profile /etc/environment 2>/dev/null
Look for a bare PATH=... assignment that overwrites rather than extends, and confirm the directory from Step 2 is listed.
Step 4: Reproduce the exact shell type that fails
# non-interactive (scripts/cron) often differ from your terminal
bash -c 'echo "$PATH"'
# login shell vs current shell
bash -lc 'echo "$PATH"'
# under sudo
sudo env | grep -i secure_path
sudo command -v <cmd>
A different PATH between these is the tell. sudo uses secure_path from /etc/sudoers, which ignores your user PATH entirely.
Step 5: Confirm the package or repair the link
# Debian/Ubuntu
apt-file search bin/<cmd>
dpkg -S "$(command -v <cmd> 2>/dev/null)"
# RHEL/Fedora
dnf provides '*/bin/<cmd>'
# clear any stale cache after fixing
hash -r
Install the package if it is missing, or hash -r and recreate the symlink if the binary moved.
Example Root Cause Analysis
A deploy script that runs fine when an engineer copies and pastes it into their terminal keeps failing in the cron job that runs it nightly:
/opt/deploy/run.sh: line 8: aws: command not found
Running command -v aws in the engineer’s interactive shell returns /home/deploy/.local/bin/aws, so the binary clearly exists. The difference is the shell type. Cron runs a minimal non-interactive shell that does not source ~/.bashrc, so the PATH export that adds ~/.local/bin never runs.
Confirming the gap by simulating cron’s environment:
env -i /bin/bash -c 'echo "$PATH"; command -v aws'
/usr/bin:/bin
~/.local/bin is absent and aws is not found — exactly what cron sees. The fix is to make the script independent of interactive shell config by setting PATH explicitly at the top of run.sh:
# top of /opt/deploy/run.sh
export PATH="/home/deploy/.local/bin:/usr/local/bin:$PATH"
After adding the line, the cron run resolves aws and completes. The interactive shell had been masking the problem all along.
Prevention Best Practices
- Always extend
PATH(PATH="/new/dir:$PATH"), never assign it bare, so inherited directories survive. - Set
PATHexplicitly inside scripts, cron jobs, and systemd units rather than relying on a login shell sourcing your rc files. - Add
secure_pathentries to/etc/sudoers(viavisudo) when a command must work undersudo, sincesudoignores your userPATH. - Use
command -vin scripts to assert a dependency exists and fail fast with a clear message instead of a raw exit 127. - Run
hash -rafter upgrades or relinking tools so the shell does not cache a path that has moved. - For broader hardening of shell environments and login profiles, see more Linux administration guides.
Quick Command Reference
# What does the shell resolve, if anything?
type <cmd>
command -v <cmd>
which <cmd>
# Does the binary exist somewhere off PATH?
ls -l /usr/bin/<cmd> /usr/local/bin/<cmd> ~/.local/bin/<cmd> 2>/dev/null
find / -name '<cmd>' -type f 2>/dev/null
# Inspect PATH and where it is set
echo "$PATH" | tr ':' '\n'
grep -nE 'PATH=' ~/.bashrc ~/.bash_profile ~/.profile /etc/profile /etc/environment 2>/dev/null
# Reproduce the failing shell type
bash -c 'echo "$PATH"' # non-interactive
bash -lc 'echo "$PATH"' # login
sudo env | grep -i secure_path
# Find the providing package
apt-file search bin/<cmd> # Debian/Ubuntu
dnf provides '*/bin/<cmd>' # RHEL/Fedora
# Clear a stale hash cache after fixing
hash -r
Conclusion
A “command not found” error is a lookup failure, not a runtime failure: the shell could not find an executable by that name on the current PATH. Work from “does it exist anywhere?” to “is its directory on PATH for this shell type?” and the cause becomes obvious. The usual root causes:
- The package providing the command is not installed.
- The binary exists but its directory is not on
PATH. - A shell rc clobbered
PATH, or a non-login/non-interactive shell built it differently. - The executable lives in a non-standard directory like
~/.local/binor/usr/local/bin. - A typo or the wrong binary name.
- A stale hash cache or broken symlink pointing at a path that moved.
Reproduce the exact shell that fails first — interactive, login, cron, or sudo — because the same host can find a command in one context and lose it in another.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.