◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-05-14Intermediate

Claude Code Doesn't Recognize nvm/pyenv/asdf — Shell Initialization and 4 Fixes

Fix 'node: command not found' and Python version mismatches in Claude Code when using nvm, pyenv, or asdf. Learn why shell initialization is skipped and how to solve it for good.

claude-code132nvmpyenvasdfPATHtroubleshooting92environment3

"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 dev

For 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.py

This 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.8

The 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.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-05-24
When Claude Code's Sequential Edits Cascade into Failure — Fixing 'old_string not found' and 'not unique' on the Same File
Run several Edits against the same file in one turn and the second or third one suddenly fails with 'not found' or 'not unique'. The cause is order-dependence inside a single context snapshot, and a Read + scoped patches usually rescue it.
⟐ Claude Code2026-05-10
When Claude Code's Read Doesn't Reach the End of a File — Catching the PARTIAL view Notice and Sizing offset/limit
When Read returns only part of a large file, Claude Code says so with a PARTIAL view notice. Here is how to catch it, and how to size offset and limit by byte budget rather than line count, with measurements from my own sandbox.
⟐ Claude Code2026-05-10
Why Claude Code's Glob and Grep Return Zero Results
When Claude Code's Glob or Grep tool returns no matches even though the files clearly exist, the cause is almost always one of four things. Here's how to triage.