Fixing "command not found" Errors - Solutions When Commands Are Missing

Fixing "command not found" Errors - Solutions When Commands Are Missing

What You'll Learn

  • Diagnose command not found errors in under 30 seconds
  • Recognize typical patterns for PATH / hash / sudo / cron
  • Stop the "I installed it but it's not found" problem from recurring

Quick Summary

  1. Check existence with command -v <name>
  2. Show search paths with echo $PATH
  3. The cause is almost always one of: missing package / PATH not set / stale hash cache / sudo secure_path / typo

Target Environment

  • OS: Ubuntu / Debian (apt). RHEL family (dnf / yum) included where relevant
  • Shell: bash / zsh

1. The 5 Root Causes

Almost every command not found error traces back to one of these:

# Cause Diagnostic key
1 Command is not installed apt-cache search / dnf provides
2 Executable is not in PATH echo $PATH / which -a
3 Shell's hash cache is stale hash -r
4 sudo's secure_path restriction sudo -V / secure_path in /etc/sudoers
5 Typo (case sensitivity / misspelling) type / compgen -c

80% of cases are #2 PATH or #3 hash. Always check those two first.

2. Diagnostic Steps (30-Second Template)

2-1. Check existence first

$ command -v ansible
  • Output present → the command is visible. If it still fails, that's a permission issue (not command not found).
  • No output → continue to 2-2.

command -v is POSIX standard. which behavior varies by distro (Debian's which only returns an exit status), so prefer command -v in scripts. Use type for investigation since it distinguishes alias / function / builtin / file.

2-2. Show PATH

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Verify that the expected install directory is in PATH. Common ones:

  • ~/.local/bin (pip install --user / pipx)
  • ~/bin (auto-added by Ubuntu's ~/.profile, but only for bash login shells)
  • /usr/local/bin (manually built tools)
  • /snap/bin (snap-installed)

2-3. Search for the package

If the command isn't installed at all, find which package provides it.

# Ubuntu / Debian
$ apt-cache search ^ansible$
$ /usr/lib/command-not-found ansible    # Ubuntu's helpful hint
# RHEL / Fedora
$ dnf provides '*/ansible'

3. Case-by-Case Fixes

3-1. Just installed but still not found

The shell has cached an old path via hash.

$ hash -r          # works in both bash and zsh
$ rehash           # zsh idiom

If that doesn't help, either the new package's bin directory isn't in PATH, or you installed it as a different user (e.g., root).

3-2. pip install --user command not found

Install location is ~/.local/bin. Add it to PATH.

$ echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc

For Python CLIs, prefer pipx. Running pipx ensurepath configures PATH automatically, so it's safer than pip install --user.

3-3. npm install -g command not found

Check the install prefix with npm config get prefix. If it's /usr/local, you may have a permissions issue. If it's a custom prefix like ~/.npm-global, add its bin to PATH.

$ npm config get prefix
$ ls "$(npm config get prefix)/bin"

3-4. Case sensitivity / typo

Linux file names are case-sensitive.

$ Vim file.txt      # → command not found
$ vim file.txt      # OK

List candidates:

$ compgen -c vim    # commands starting with "vim"

3-5. Not found after switching shells

When switching bashzsh, the file where PATH is configured changes.

Shell Login shell Interactive shell
bash ~/.bash_profile~/.profile ~/.bashrc
zsh ~/.zprofile ~/.zshrc

~/.profile is read by POSIX shells. ~/.bashrc is bash-only and is not read by zsh. If you switch to zsh, move your PATH settings to ~/.zshrc.

4. Persisting PATH the Right Way

# At the end of ~/.bashrc or ~/.zshrc
export PATH="$HOME/.local/bin:$PATH"
  • Order (before vs. after $PATH) controls priority
  • Put your custom scripts before $PATH to override system commands
  • Put them after to let system commands win

4-2. System-wide PATH

Use /etc/profile.d/*.sh. Note that /etc/environment only accepts variable assignments (no shell syntax).

# /etc/profile.d/myapp.sh
export PATH="/opt/myapp/bin:$PATH"

5. Not Found Under sudo / cron

5-1. command not found with sudo

sudo overrides PATH with secure_path from /etc/sudoers.

$ sudo -V | grep -i path
Value to override user's $PATH with: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Fixes:

# (1) Use the full path (most reliable)
$ sudo /home/user/.local/bin/myscript

# (2) Carry over the environment with sudo -E (may be ignored when secure_path is set)
$ sudo -E env "PATH=$PATH" myscript

# (3) Permanent fix: edit secure_path with visudo
$ sudo visudo
# Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/user/.local/bin"

secure_path exists to prevent PATH-injection attacks during privilege escalation. Don't widen it carelessly — only add the directories you actually need.

5-2. command not found in cron

cron starts with a minimal PATH.

$ env -i sh -c 'echo $PATH'
/usr/bin:/bin

Fixes (in order of preference):

# (1) Declare PATH at the top of crontab
PATH=/usr/local/bin:/usr/bin:/bin:/home/user/.local/bin
* * * * * myscript

# (2) Use absolute paths in the script
* * * * * /home/user/.local/bin/myscript

70% of cron problems are PATH. The next biggest issue is unredirected stdout/stderr. Always append >> /tmp/cron.log 2>&1 so you can see what's happening.

6. Copy-Paste Diagnostic Template

# 1. Check existence
command -v ansible

# 2. Show PATH
echo $PATH

# 3. Clear hash (the go-to fix right after install)
hash -r
command -v ansible

# 4. Search for the package
apt-cache search ^ansible$       # Debian / Ubuntu
dnf provides '*/ansible'         # RHEL / Fedora

# 5. Check under sudo
sudo command -v ansible
sudo -V | grep -i path

Things you should never do

  • Overwrite PATH by assigning PATH= with no existing value
  • Put the same PATH export in both ~/.bash_profile and ~/.profile (one gets ignored)
  • Run chmod 777 reflexively when sudo fails

Next Reading