What to do when a CLI tool shows “command not found” after installation

    A practical guide to diagnosing and fixing the “command not found” error after installing a CLI tool. It explains how shells resolve commands, why adding ~/.local/bin to PATH often fails, and provides a five-step troubleshooting workflow for macOS, Linux, and Windows.

    1. Problem Background: You Followed the Instructions, but the Error Is Still There

    The documentation for most command-line tools includes a common final step: install the tool, then run the command name in your terminal to verify the version. However, many users complete the installation successfully, run the expected command, and still see a message like this:

    1. <your-shell-name>: command not found: <tool-name>
    2. # On Windows Command Prompt, the message is usually:
    3. # '<tool-name>' is not recognized as an internal or external command,
    4. # operable program or batch file.

    This is one of the most common command-line errors. It may look like a simple “installation failed” issue, but it can actually involve several different factors: how the tool was installed, where the executable was placed, environment variables, shell configuration files, the order in which those files are loaded, and even the presence of multiple installed versions.

    One of the most widely shared fixes is to add the user-level installation directory ~/.local/bin to the shell configuration file, for example:

    1. echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc

    This command is not wrong by itself. The problem is that it only applies to a specific installation scenario. It assumes that the tool was installed into ~/.local/bin by an official installation script. In reality, the installation location varies depending on the installation method. If the tool was installed through a system package manager or a language-specific package manager, adding a directory that does not contain the executable to PATH will not solve the problem.

    Worse still, copying this command blindly may leave multiple duplicate or conflicting PATH entries in the shell configuration file, making later troubleshooting more difficult.

    Therefore, a better approach is to split the problem into two independent questions:

    1. Locating the problem: Where is the executable file actually located, and was it installed at all?
    2. Resolving the problem: Which directories does the shell search when looking for a command, in what order does it search them, and which configuration files does it load?

    Once these two questions are answered separately, the fix becomes precise and can also be reused for other command-line tools.

    2. How the Shell Finds a Command

    In Unix-like systems, when you type a command in the terminal and press Enter, the shell resolves it according to a fixed order:

    1. Whether it is a defined or loaded alias.
    2. Whether it is a shell function.
    3. Whether it is a built-in command, such as cd or export.
    4. Whether it matches a command location cache. Some shells record the paths of previously executed commands to speed up later lookups.
    5. Whether it matches a file with the same name and executable permission in one of the directories listed in the PATH environment variable, searched in order.
    6. If no match is found in any directory, the shell prints command not found and exits with status code 127.

    Step 5 is the most important. PATH is a colon-separated list of directories. The shell searches from left to right and stops at the first file with a matching name. From this, two important conclusions can be drawn:

    • A command can be found only if two conditions are met: the file actually exists, and its directory is included in the PATH of the current shell process.
    • A command can run only if two conditions are met: the process has permission to access the file, and the file is compatible with the current system.

    In other words, PATH only solves the question of where to look for a command. If the file is not in any directory listed in PATH, changing PATH may be necessary. However, if the file is already in PATH but still cannot run, the problem is usually related to permissions, architecture, or dependencies.

    The command-line environments in Windows work in a similar way, but with a few differences:

    • When looking for a command, Windows can use file extensions listed in the PATHEXT environment variable, such as .EXE, .BAT, .CMD, and others.
    • Environment variables are divided into user-level and system-level variables. After modification, the new values take effect only when a new process starts.
    • Command Prompt and script-oriented shells such as PowerShell load environment variables and script files differently.

    3. Why Adding ~/.local/bin to PATH Often Does Not Work

    3.1 The Installation Method Determines the Installation Location

    After a tool is installed, the location of its executable file mainly depends on how it was installed. Below is a common mapping between installation methods and installation locations:

    Installation Method Common Executable Location Included in PATH by Default?
    Official one-click installation script A local bin directory under the user’s home directory, such as ~/.local/bin Depends on the script; not guaranteed
    System package manager on ARM-based Macs The bin subdirectory of the package manager’s installation prefix, such as /opt/homebrew/bin Usually yes
    System package manager on x86-based Macs /usr/local/bin Usually yes
    Global installation by a language-level package manager The global bin directory of the package manager, which may vary with the language runtime version Depends on whether the installer updates PATH automatically
    Language runtime version manager A bin directory under each version, or through forwarding files, also called shims Depends on whether version switching works correctly
    Linux distribution package repositories /usr/bin or /usr/local/bin Yes
    Inside a container image The installation path predefined by the container image Depends on the environment configuration of the image
    Graphical installer, such as an installer on Windows The program installation directory, such as an application folder under the user data directory Depends on whether “Add to PATH” is selected during installation

    From this table, it is clear that only tools installed by an official one-click installation script are usually placed in ~/.local/bin. If a tool is installed through a system package manager, the executable is usually placed in the bin subdirectory of the package manager’s installation prefix. For example, on ARM-based Macs this may be /opt/homebrew/bin, while on x86-based Macs it may be /usr/local/bin.

    In that case, adding ~/.local/bin to PATH is equivalent to adding an empty directory to the search list, and the error will remain.

    3.2 The PATH Setting Was Written to a File That Is Not Read

    Different shell startup scenarios automatically read different configuration files:

    • A new terminal window opened by a terminal application is usually a non-login interactive shell.
    • A session connected to a remote server through SSH is usually a login shell.
    • Commands executed by scripts, scheduled tasks, or continuous integration pipelines belong to non-interactive shells, which by default do not read user-level configuration files.

    Different shells also have different default behaviors. For example, on macOS, where zsh is often the default shell, login sessions read /etc/zprofile, ~/.zprofile, /etc/zshrc, and ~/.zshrc in order. On many Linux distributions where bash is the default shell, login sessions read /etc/profile along with ~/.bash_profile, ~/.bash_login, or ~/.profile. If multiple of these files exist, only the first one is read.

    In both cases, ~/.bashrc is not automatically read.

    If a configuration is written into the wrong file during troubleshooting, a situation may occur where the command works in the terminal but cannot be found after logging in through SSH. Similarly, on Windows, if environment variables are modified but a new command-line window is not opened, or if the modification was made without administrator privileges, the change may appear to have no effect.

    3.3 Changes Do Not Take Effect in the Current Process

    Environment variables are inherited from the parent process when a process is created. They are not automatically synchronized afterward. After modifying a configuration file or a system environment variable, only newly started shells will load the new values.

    There are four common ways to verify the result:

    1. # 1. Reload the current shell configuration file. The exact filename depends on the shell in use.
    2. source ~/.zshrc
    3. # 2. Add a directory temporarily for the current session only. This is the safest way to verify.
    4. export PATH="$HOME/.local/bin:$PATH"
    5. # 3. Show the PATH currently used by the shell.
    6. echo "$PATH" | tr ':' '\n'
    7. # 4. Clear the command location cache. Some shells provide a built-in command such as hash -r.
    8. hash -r 2>/dev/null || true

    Another easily overlooked situation is that applications launched from a graphical interface inherit the environment that existed when the graphical session started, not the configuration modified later. Therefore, when opening an embedded terminal inside a graphical application or code editor, the terminal may still receive the old PATH value.

    3.4 Multiple Versions Coexist: The Tool Is Installed but Does Not Work as Expected

    If multiple executable files with the same name exist on the system, the shell will run only the first one found in PATH. Common scenarios include:

    • A tool was first installed globally through a language-level package manager, then installed again through a system package manager, but the old entry was not cleaned up.
    • A language runtime version manager and a package manager each provide an entry, but the version pointed to by the forwarding file has been removed or renamed.
    • An old version was installed into a system directory with administrator privileges, and system directories usually appear earlier in PATH.

    The visible symptom may be “command not found” or “wrong version,” but the real issue is that multiple versions are interfering with each other.

    3.5 Non-PATH Issues: The File Exists but Cannot Be Executed

    The following types of problems are often misdiagnosed as PATH issues:

    • Missing executable permission: If a file is extracted from an archive or copied from another file system, the executable bit may be lost. The shell can find the file but reports a permission error, such as Permission denied.
    • Architecture mismatch: For example, a file compiled for ARM architecture is run on a system that only supports x86 architecture, or the required architecture compatibility layer is missing.
    • Missing dependencies: Required dynamic libraries are missing, the language runtime version is insufficient, or dependency downloads failed during installation, meaning the installation was not actually completed.
    • Incorrect file type: A package manager may create a forwarding file, also called a shim, that points to the actual entry point. If the target file is deleted, the forwarding file becomes a broken link.

    4. Solution: A Five-Step Troubleshooting Workflow

    Step 1: Read the Error Message Carefully and Identify the Failure Type

    Different errors correspond to different root causes. Start by matching the symptom:

    Error Feature Possible Cause Where to Look
    command not found, exit code 127 The file does not exist, or its directory is not in PATH Step 2 and Step 3
    Permission denied The file exists but does not have executable permission Step 5
    cannot execute binary file or an architecture mismatch error The file architecture is incompatible with the system Step 5
    The command runs but reports a version error or missing dependencies The runtime environment or dependencies are missing Step 5
    On Windows, the message says the command is not recognized as an internal or external command The directory is not in PATH, or the required extension is missing Step 2 and Step 3
    Different terminals behave differently The configuration files are loaded in different scenarios Step 3

    Step 2: Check the Current Process Resolution Result and Environment

    First, confirm whether the shell recognizes the name and what the actual PATH value is:

    1. # Show the resolution result: alias, function, cached path, or search path.
    2. type -a <tool-name>
    3. # Show each directory in PATH line by line, including its order.
    4. echo "$PATH" | tr ':' '\n' | nl
    5. # Check whether an alias or function with the same name exists. It may hide the real command.
    6. alias | grep -i '<tool-name>' || true

    On Windows Command Prompt, similar checks can be performed with the following commands:

    1. echo %PATH%
    2. where <tool-name>

    If you find an alias or function with the same name that was not intentionally configured, you can remove it first and then verify again:

    1. unalias <alias>
    2. unset -f <function>

    Step 3: Locate the Real Position of the Executable File

    This is the key step in the entire process. If the previous step shows that the command is not in PATH, check common installation directories one by one:

    1. # Official one-click installation script.
    2. ls -l ~/.local/bin/<tool-name>
    3. # System package manager on ARM-based Macs.
    4. ls -l /opt/homebrew/bin/<tool-name>
    5. # System package manager on x86-based Macs.
    6. ls -l /usr/local/bin/<tool-name>
    7. # Linux distribution package repositories.
    8. ls -l /usr/bin/<tool-name> /usr/local/bin/<tool-name>
    9. # Global installation by a language-level package manager.
    10. # First query the global package list, then check the global bin directory.
    11. <package-manager> -g ls <package-name>
    12. <package-manager> bin -g

    You can also search directly in the file system:

    1. find ~ /usr/local /opt -type f -name '<tool-name>' 2>/dev/null
    2. find ~ /usr/local /opt -type l -name '<tool-name>' 2>/dev/null # Find forwarding files.

    Replace <package-manager> and <package-name> with the package manager actually used and the package name it provides. To reduce irrelevant output, you can limit the search to the user directory and system-level installation prefixes.

    The search results should be interpreted as follows:

    • If any command outputs the file, the directory containing that file is the directory that should be added to PATH.
    • If the output includes ->, the file is a forwarding file, and you need to continue checking whether the target file it points to exists.
    • If the output reports insufficient permissions, you can run the command again with administrator privileges.
    • If none of the commands produce output, the tool may not have been installed successfully. In that case, reinstall it and pay attention to the installation logs.

    On Windows, you can check the following locations in File Explorer or Command Prompt:

    • The local application folder under the user data directory.
    • The installation directory selected during program installation.
    • The global directory of a language-level package manager, usually a subdirectory inside the application data folder under the user data directory.
    • System-level installation directories, such as C:\Program Files.

    Step 4: Choose the Appropriate Fix Based on the Result

    Based on the result of Step 3, choose one of the following four cases.

    Case 1: The directory is found, but it is not in PATH

    Write the directory into the correct shell configuration file. For example, with an official installation script:

    1. # Only append the directory if it exists to avoid adding an invalid path.
    2. if [ -d "$HOME/.local/bin" ]; then
    3. case ":$PATH:" in
    4. *":$HOME/.local/bin:"*) ;; # Do nothing if it already exists.
    5. *) export PATH="$HOME/.local/bin:$PATH" ;; # Add it to the beginning of PATH.
    6. esac
    7. fi

    Choosing the configuration file:

    • For users on macOS whose default shell is zsh, appending the setting to ~/.zshrc is sufficient for daily terminal use. If you also need to cover login-shell scenarios, you can append it to ~/.zprofile instead.
    • For Linux distributions whose default shell is bash, append the setting to ~/.bashrc, and confirm that ~/.bash_profile includes logic to load ~/.bashrc. Most distributions already include this by default.

    When modifying PATH, follow these principles:

    • Place user-level directories before system directories so that personally installed versions take priority.
    • Make the change idempotent. Either check before appending, as shown above, or place the configuration in a separate file and reference it only once from the shell configuration file.
    • Back up the original file before making changes so that you can roll back if something goes wrong.

    If the tool was installed by a package manager and the installer did not configure PATH automatically, query the global bin directory and add it to the configuration in the same way.

    On Windows, you can use the “Environment Variables” interface in system settings to add the target directory to the user-level Path variable. You can also use the setx command in an administrator-privileged command prompt:

    1. rem Append the directory to the user-level Path. Keep the total length under 1024 characters.
    2. setx Path "%Path%;<install-directory>"

    Note that setx has a 1024-character length limit and writes the merged PATH value of the current process into the user variable. If the current PATH is already long, it is usually better to edit it precisely through the graphical interface. In either case, a new command-line window must be opened for the change to take effect.

    If the target directory is the global bin directory of a language-level package manager, prefer using the package manager’s own configuration command. For example, some package managers provide a dedicated command for configuring the global bin directory. Such a command can write PATH and also adjust the package manager’s installation prefix, preventing global packages from being installed into an old directory.

    Case 2: The command is already in PATH and can run normally

    This means the environment itself is not the problem. If an error still occurs, check the following in order:

    • Whether the current window was opened before the configuration was modified.
    • Whether the invocation includes an extra prefix, such as accidentally running <tool-name>/<tool-name>.
    • Whether the command name contains extra spaces or full-width characters.

    Case 3: The file exists, but a permission or architecture error occurs

    1. # Grant executable permission. Replace the example with the located file.
    2. chmod +x ~/.local/bin/<tool-name>
    3. # Check the file type and architecture information.
    4. file ~/.local/bin/<tool-name>
    5. uname -m

    If the output of file shows that the file architecture does not match the result of uname -m, reinstall the tool with an installation package that matches the local architecture, or enable the appropriate architecture compatibility layer.

    Case 4: The file is not found

    This means the tool has not been installed successfully. Reinstall it and pay attention to messages during installation. If the installation process requires downloading dependencies over the network, check whether the network and proxy settings are correct. After installation, repeat Step 3 to locate the executable again.

    After fixing the issue, open a new terminal window and verify:

    1. <tool-name> --version
    2. which <tool-name>

    Step 5: Handle Multiple Installations and Residual Configurations

    If multiple entries exist, first list all candidate paths:

    1. # List all matching files found in PATH. This option is supported by some shells.
    2. which -a <tool-name>
    3. # When using a language runtime version manager, show the currently active version.
    4. <version-manager> current

    After confirming which version to keep, handle the issue in this order: disable first, clean up next, and verify last.

    1. Temporarily verify: Put the target directory at the beginning of PATH temporarily and confirm that the desired version works.
    2. Remove extra versions: Uninstall old entries using the corresponding package manager. Avoid deleting files directly, because that may leave broken forwarding files.
    3. Clean duplicate lines in configuration files: Manually edit the shell configuration file and merge duplicate PATH settings.
    4. Verify again: Open a new terminal and confirm that which <tool-name> returns only one expected path.

    If multiple versions need to be preserved, for example when different projects require different versions, it is better to introduce a version management mechanism. In that case, the PATH can be managed uniformly through the forwarding directories of each version, and the active version can be selected by switching versions rather than manually modifying PATH repeatedly.

    5. Best Practices: Preventing command not found from the Start

    1. Prefer official installation methods or package managers. Official installation scripts usually handle PATH automatically, and binaries installed by package managers are usually placed in directories that are already in PATH. Both approaches are less likely to cause the “installed but not found” problem. After installation, verify immediately with a version command.
    2. Manage PATH settings centrally. Put all PATH settings into a separate file, such as a shell configuration fragment in the user directory, and reference it only once from the shell configuration file. This avoids scattered modifications and unintended overrides.
    3. Make configuration changes idempotent. Before appending a directory, check whether it exists and whether it is already in PATH. This prevents duplicate entries from being added every time a terminal is opened.
    4. Use absolute paths in scripts and scheduled tasks. Non-interactive environments do not load user-level configuration files. Scripts should use the full path to a command or explicitly set the required environment variables so that they can run correctly in scheduled tasks, continuous integration, and similar environments.
    5. Keep cloud development environments consistent. In scenarios such as cloud hosts or containerized development environments, tools are often installed in paths predefined by the container image or in mounted data disk directories. The troubleshooting approach is the same as for local environments: first locate the file, then check the PATH of the current process, and finally write the setting into the correct configuration file.

    6. Summary: A command not found Checklist

    Check Item How to Check Corresponding Action
    Was the installation successful? Search for the file in common installation directories. Reinstall if the tool is not installed.
    Is the file in the current process’s PATH? Compare the file’s directory with the contents of PATH. Add the directory to the correct configuration file.
    Was the configuration written to the correct file? Confirm the loading rules for terminals, login shells, and non-interactive scenarios. Adjust the configuration file and loading logic.
    Did the change take effect? Open a new window or reload the configuration. Restart the shell and clear the command cache.
    Do multiple versions conflict? Use which -a and check the active version. Remove extra versions or enable version management.
    Can the file itself run? Use file to check the architecture and ls -l to check permissions. Fix permissions or install a version matching the architecture.

    The command not found error may look like a minor issue, but it actually tests both the installation mechanism of command-line tools and the command resolution mechanism of the shell. If you follow the order “read the error, check the process, locate the file, verify the configuration, and remove conflicts,” most cases can be diagnosed and fixed quickly.

    The next time you see this message, do not rush to copy an export command from the internet. Start with which and ls; the problem is often simpler than it appears.