Why Zsh Reports "node: command not found" When Using asdf-nodejs (And How to Fix It)
Zsh caches command locations in an internal hash table, so if you install or switch Node.js versions after starting your shell, Zsh may still look for the binary in its old location until you run rehash or restart the shell.
When managing multiple Node.js versions with asdf-nodejs, you might encounter a frustrating scenario: asdf current nodejs confirms a version is installed and active, yet Zsh responds with zsh: command not found: node. This issue stems from how the Zsh shell interacts with asdf's shim system rather than from the Node.js runtime itself, which resides in the nodejs/node repository and enters execution through src/node.cc.
How asdf-nodejs Manages Node.js Versions
The asdf version manager installs each Node.js version into isolated directories under ~/.asdf/installs/nodejs/. For example, version 20.12.0 lives at:
~/.asdf/installs/nodejs/20.12.0/bin/node
Rather than modifying your $PATH every time you switch versions, asdf creates a shim—a thin wrapper executable at ~/.asdf/shims/node. When you type node, the shell should find this shim, which then reads the active version from ~/.tool-versions and delegates to the actual binary in the installs directory.
Why Zsh Cannot Find the node Command
The Zsh Command Hash Table
Zsh maintains an internal hash table that maps command names to their absolute file system paths. When you first type node, Zsh searches $PATH, finds the shim at ~/.asdf/shims/node, and stores this location in its hash table for faster subsequent lookups.
If you later install a new Node.js version, change the global version with asdf global nodejs, or open a new terminal tab where the version differs, the shim itself remains at the same path. However, if the shim was removed, regenerated, or if the shell's hash somehow points to a stale location (such as a previous manual installation that no longer exists), Zsh will report "command not found" because its cached lookup fails before it even searches $PATH again.
Common Scenarios That Trigger the Error
- Installing Node.js after starting the shell: You opened Zsh, then ran
asdf install nodejs 20.12.0. The shim is created, but Zsh's hash table was built before the shim existed. - Switching versions in an existing session: You changed the active version with
asdf globalorasdf local, but Zsh still holds the old hash entry. - Missing initialization in
.zshrc: The asdf setup script that adds~/.asdf/shimsto$PATHwas never sourced, so Zsh has no way to discover the shim.
Solutions to Fix "node: command not found" in Zsh
Refresh the Command Hash with rehash
The fastest fix is to force Zsh to rebuild its command hash table. Run either:
rehash
Or the more explicit:
hash -r
After executing this command, Zsh will search $PATH anew the next time you type node, locating the shim at ~/.asdf/shims/node.
Ensure asdf Initialization is in Your Zsh Configuration
If the error persists after rehashing, verify that asdf's setup script is properly sourced in your ~/.zshrc file. Add these lines if they are missing:
. $HOME/.asdf/asdf.sh
. $HOME/.asdf/completions/asdf.bash
The first line prepends ~/.asdf/shims to your $PATH and sets up the asdf function. Without this, Zsh cannot discover the shim directory at all.
Regenerate Shims with asdf reshim
In some cases, particularly after manually compiling Node.js or installing global npm packages that add binaries, the shim for node may be missing or corrupted. Force asdf to recreate all shims:
asdf reshim nodejs
This scans the ~/.asdf/installs/nodejs/ directory and recreates the wrapper scripts in ~/.asdf/shims/.
How the Node.js Binary is Executed
Once Zsh successfully locates the shim, the execution flow proceeds to the actual Node.js runtime. The shim at ~/.asdf/shims/node is a small script that reads the version specified in ~/.tool-versions and executes the corresponding binary, such as:
~/.asdf/installs/nodejs/20.12.0/bin/node
This binary is the compiled output of the nodejs/node repository. The interpreter's entry point is defined in src/node.cc, which initializes the V8 JavaScript engine, sets up the event loop, and begins executing your JavaScript code. When the shim delegates to this binary, the Node.js runtime launches as expected, provided the shell could find the shim in the first place.
Summary
- asdf-nodejs uses shims in
~/.asdf/shims/to delegate to version-specific binaries stored in~/.asdf/installs/nodejs/. - Zsh caches command paths in an internal hash table; if the
nodeshim appears or moves after the shell starts, Zsh may report "command not found" until the cache is cleared. - Fix the issue by running
rehash(orhash -r), ensuring~/.zshrcsourcesasdf.sh, or runningasdf reshim nodejs. - The actual Node.js runtime entry point is
src/node.ccin thenodejs/noderepository, executed once the shim successfully delegates to the version-specific binary.
Frequently Asked Questions
Why does asdf current show a Node.js version but the shell cannot find it?
asdf current reads the version configuration from .tool-versions files and the asdf installation directory independently of the shell's command resolution. If Zsh's internal command hash table contains a stale entry or if the asdf shim directory is not in your $PATH, the shell will fail to locate the node executable even though asdf knows which version should be active.
What is the difference between rehash and hash -r in Zsh?
Both commands force Zsh to rebuild its internal command hash table, causing the shell to search $PATH again for command locations the next time you execute them. rehash is the Zsh-specific built-in command, while hash -r is the POSIX-compatible syntax that also works in Bash and other shells. In Zsh, they are functionally equivalent for clearing the command cache.
When should I use asdf reshim nodejs instead of just rehashing?
Run asdf reshim nodejs when the shim executable itself is missing from ~/.asdf/shims/ or when you have installed global npm packages that provide new binaries. Rehashing only updates Zsh's cache of where commands are located; it cannot help if the shim file does not exist. Reshiming regenerates all wrapper scripts in the shims directory based on the current contents of the ~/.asdf/installs/nodejs/ directory.
Does this issue affect Bash or other shells besides Zsh?
While this article focuses on Zsh, Bash can exhibit similar behavior through its own command hashing mechanism (hash -r also works in Bash). However, Zsh is more aggressive about caching command locations and is the default shell on macOS since Catalina, making this issue particularly common for Zsh users. Fish shell handles PATH changes differently and typically does not require manual rehashing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →