"Run npm run dev" — and Claude Code returns node: command not found.
Your local terminal works fine. The version manager is set up correctly. Yet every time Claude Code executes a shell command, it seems to forget that nvm, pyenv, or asdf even exists.
I've been developing indie apps since 2014, and environment variable issues have tripped me up more times than I'd like to admit. After working through this particular problem across multiple projects, I can say the fix is straightforward once you understand what's actually happening.
Why Claude Code Can't See Your Version Manager
When Claude Code runs Bash commands, it spawns a non-interactive, non-login shell. This means it doesn't execute your shell's initialization scripts: .bashrc, .zshrc, .bash_profile, or .zprofile.
Every version manager works the same way under the hood. It hooks into your shell's startup sequence. For nvm, that looks like this in your .zshrc:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"When you open a terminal, this runs automatically, adding the nvm-managed Node.js binary to your PATH. Claude Code's subprocess never runs this initialization, so it falls back to whatever node (if any) lives in the default system PATH — typically an older system-installed version, or nothing at all.
Diagnose Your Environment First
Before applying any fix, confirm the issue with these commands in Claude Code:
# What PATH does Claude Code's subprocess see?
echo "PATH: $PATH"
which node 2>/dev/null || echo "node: not found"
which python3 2>/dev/null || echo "python3: not found"
node --version 2>/dev/null || echo "node version: not found"
python3 --version 2>/dev/null || echo "python3 version: not found"Run the same commands in your local terminal and compare. If the PATH values are significantly different — especially if you don't see paths like /home/user/.nvm/versions/node/v20.x.x/bin — that confirms the problem.
Fix 1: Use Absolute Paths (Fastest Resolution)
The quickest workaround is bypassing the version manager entirely and calling the binary directly.
For nvm:
# List your installed versions
ls ~/.nvm/versions/node/
# Use the absolute path
~/.nvm/versions/node/v20.11.0/bin/node --version
~/.nvm/versions/node/v20.11.0/bin/npm install
~/.nvm/versions/node/v20.11.0/bin/npm run devFor pyenv:
# List your Python versions
ls ~/.pyenv/versions/
# Use the absolute path
~/.pyenv/versions/3.11.8/bin/python3 -m pip install requests
~/.pyenv/versions/3.11.8/bin/python3 script.pyThis works immediately but has a downside: when you update your Node or Python version, you'll need to update these paths too. It's a solid temporary fix, but not something you want to maintain long-term.
Fix 2: Document the Paths in CLAUDE.md
Add the resolved paths to your project's CLAUDE.md file. Claude Code reads this file at session start, so once it's there, Claude will use the correct paths consistently across the project.
## Development Environment
This project uses the following environment:
- Node.js: v20.11.0 (managed via nvm)
- `node` binary: `~/.nvm/versions/node/v20.11.0/bin/node`
- `npm` binary: `~/.nvm/versions/node/v20.11.0/bin/npm`
- Python: 3.11.8 (managed via pyenv)
- `python3` binary: `~/.pyenv/versions/3.11.8/bin/python3`
When running commands, either use absolute paths above or load the environment first:
```bash
export PATH="$HOME/.nvm/versions/node/v20.11.0/bin:$HOME/.pyenv/versions/3.11.8/bin:$PATH"
This approach benefits everyone working in the same repository. New team members won't hit the same confusion, and Claude Code will always know where to look.
## Fix 3: Wrap Commands in a Login Shell
For cases where you need a specific command to fully inherit your shell environment:
```bash
# Force login shell initialization
bash -l -c "node --version"
bash -l -c "npm run dev"
# zsh equivalent
zsh -l -c "python3 manage.py runserver"
A caveat: Claude Code executes each Bash tool call as a separate process, so you'd need the bash -l -c "..." wrapper every time. If you find yourself using this frequently, Fix 4 is worth considering.
Fix 4: Switch to volta or mise (Long-Term Solution)
The root cause here is that nvm, pyenv, and asdf all rely on shell initialization to function. The permanent fix is switching to a version manager that doesn't have this dependency.
volta manages Node.js at the project level via package.json. Any process that reads package.json — including Claude Code's subprocesses — automatically gets the right version:
{
"volta": {
"node": "20.11.0",
"npm": "10.2.4"
}
}mise (formerly rtx) supports Node.js, Python, Go, Ruby, and more. It uses a .tool-versions file at the project root, and versions are resolved per-process without relying on shell hooks:
# .tool-versions
nodejs 20.11.0
python 3.11.8The migration takes some time, but once done, this class of problem disappears. I made the switch to volta for my wallpaper and wellness app projects — the kind that require juggling multiple Node versions — and haven't lost time to PATH issues since.
Which Fix Is Right for You?
- Need it working right now: Fix 1 (absolute paths)
- Working on a specific project with a team: Fix 2 (CLAUDE.md documentation)
- Long-term, cross-project solution: Fix 4 (switch to volta or mise)
Start with Fix 2 if you're in a project context. Add a few lines to CLAUDE.md, specify the paths, and Claude Code will handle the rest consistently without requiring you to think about it again.
One thing I've found useful: add the environment setup to CLAUDE.md early in any new project, before the problem surfaces. It takes two minutes and saves the frustration of debugging command not found errors mid-session.