You upgrade Claude Code, type claude --version, and the same number from last week stares back at you. If you've spent thirty minutes reinstalling and re-reinstalling without anything moving, you're not alone — I've burned that exact afternoon myself, all because I was reinstalling the wrong binary.
When auto-update appears to fail, the cause almost always falls into one of three buckets: a PATH conflict, a stale local cache, or a shell environment that quietly switches Node versions on you. This guide gives you a five-minute triage flow before you reach for a clean reinstall, plus targeted fixes for each scenario.
Run these three lines first
Before anything else, drop these into your terminal. Most people find their answer right here.
# Which binary is actually being invoked
which claude
# Where npm puts global packages
npm root -g
# Claude Code's own self-diagnosis
claude doctorIf the path returned by which claude doesn't sit under the parent of npm root -g (i.e., the directory whose bin/ would contain it), then the binary you just upgraded with npm install -g is not the one your shell runs. This is by far the most common reason "I just updated but nothing changed" happens.
Why this keeps happening — three common shapes
1. Homebrew and npm both installed it (frequent on macOS)
If you ran brew install claude-code (or a similar formula) at some point and later switched to npm — or vice versa — both /opt/homebrew/bin/claude and ~/.npm-global/bin/claude end up on your PATH. Whichever appears earlier in echo $PATH wins, so updating the npm copy doesn't help if Homebrew's stale binary is still found first.
2. nvm is silently using a different Node
If you manage Node with nvm, every Node version has its own global package directory at ~/.nvm/versions/node/v22.x.x/bin/. Run npm install -g while on Node 20, then later nvm use 22 and call claude — you'll be running a binary that was never installed (or installed at an older version) under v22.
3. A broken ~/.claude/local self-update cache
In some versions, Claude Code keeps its current binary in ~/.claude/local/ and runs from there. If that cache becomes corrupted — usually after a network blip during self-update — the launcher prefers the cached copy over what npm just installed.
A staged diagnosis flow
If those three lines didn't reveal the culprit, here's a more systematic walk-through. Following these in order will surface the issue in nearly every case.
Step 1: List every claude binary on your PATH
type -a claude
# Example output:
# claude is /opt/homebrew/bin/claude
# claude is /Users/you/.npm-global/bin/claudeUnlike which, type -a lists every candidate on your PATH. Two or more entries is your smoking gun.
Step 2: Check each binary's version directly
/opt/homebrew/bin/claude --version
/Users/you/.npm-global/bin/claude --versionCalling each absolute path tells you which one is up to date and which one your shell has been calling all along.
Step 3: Check PATH ordering across shell config files
grep -nE "PATH" ~/.zshrc ~/.bashrc ~/.bash_profile ~/.profile 2>/dev/nullA surprisingly common pattern: you append your preferred path in ~/.zshrc, but ~/.zprofile later prepends another one and overrides you. Check all of them.
Step 4: Run the official self-diagnosis
claude doctorclaude doctor checks the Node version it depends on, settings file integrity, and authentication state in one pass. Newer versions also surface explicit warnings like "Auto-update path conflict" along with the offending paths.
Fixes by scenario
Case A: Homebrew × npm dual install
I've made this mistake more than once myself. The cleanest answer is to commit to a single install path. Anthropic's documentation steers users toward npm, so here's the snippet to drop the Homebrew copy.
# Drop the Homebrew copy to consolidate
brew uninstall claude-code 2>/dev/null
# Wipe any stragglers
sudo rm -f /opt/homebrew/bin/claude /usr/local/bin/claude
# Reinstall via npm
npm install -g @anthropic-ai/claude-code@latest
# Verify
which claude
claude --versionA judgment call: Some teams genuinely prefer Homebrew for centralized management. The trade-off is that Homebrew formulae lag the upstream Anthropic release by a beat or two. If you want new features the day they ship, npm gives you the smoother experience.
Case B: You're using nvm
Under nvm, global packages are partitioned per Node version. The pragmatic move is to pin the version you actually use.
# Pin your daily Node version
nvm alias default 22
nvm use 22
# Then reinstall
npm install -g @anthropic-ai/claude-code@latest
# Verify: the path should contain v22.x.x
which claudeIf you genuinely need to juggle multiple Node versions, make sure your shell's startup runs nvm use <version> so you don't quietly drop back to a Node where Claude Code isn't installed.
Case C: Corrupted ~/.claude/local cache
When the self-update cache is the suspect, deleting it is faster than debugging it. Your settings (~/.claude/settings.json) and credentials live elsewhere, so wiping local/ won't log you out.
# Clear only the self-update cache; settings/auth untouched
rm -rf ~/.claude/local
# Launch and let it re-download
claude --versionIf launch hangs at "Downloading local cache…" for a long time, you're likely in Case D below.
Case D: Corporate proxy or self-signed SSL blocking the update
On a managed corporate network, auto-update can fail silently. The npm install itself succeeds, but the direct update endpoint Claude Code hits gets blocked at the proxy.
# Inspect npm's proxy settings
npm config get proxy
npm config get https-proxy
# If you need a corporate CA bundle
export NODE_EXTRA_CA_CERTS=/path/to/corp-ca-bundle.pem
# Or simply turn off auto-update and update manually
# settings.json: "autoUpdates": falseSetting autoUpdates: false stops the silent retry storm and gives you predictable behavior: versions only change when you intentionally run npm install -g @anthropic-ai/claude-code@latest. For the full settings reference, see the Claude Code settings.json complete guide.
Case E: When nothing else works
If you've worked through every case above and still see the old version, your install has probably accreted too much state. Wipe it cleanly and start over.
# Remove everything (this clears credentials too — see the backup line below)
npm uninstall -g @anthropic-ai/claude-code
sudo rm -f $(which -a claude)
rm -rf ~/.claude/local
# Save credentials beforehand if you want a quick recovery
cp ~/.claude/auth.json ~/claude-auth.backup.json 2>/dev/null
# Clean install
npm install -g @anthropic-ai/claude-code@latest
claude --versionWith ~/.claude/auth.json backed up, restoring auth takes about thirty seconds. If you still see no change after this, double-check node --version — Node older than 18 can produce surprising behavior.
When you actually want to disable auto-update
In CI/CD or production scripts, surprise version bumps are bad. Disable explicitly:
In ~/.claude/settings.json (or .claude/settings.json at the repo root):
{
"autoUpdates": false
}This stops the startup update check. To bump versions, you run npm install -g @anthropic-ai/claude-code@<version> deliberately. For locked-down CI, I personally prefer pinning to a minor like @anthropic-ai/claude-code@2.1.4 so reproducibility is rock-solid.
For longer-running CI workflows, the version-pinning patterns in Claude Code output format JSON × CI/CD integration are also worth a look.
Look-alike issues that actually have different causes
Two more pitfalls I've seen confuse people into thinking auto-update is broken when it isn't.
- A new tool is gated by
allowedTools: If yoursettings.jsonhas anallowedToolswhitelist, newly added tools won't activate until you add them. The CLI version is new but the behavior is old — classic look-alike. - Your shell has cached the binary path: zsh and bash cache executable lookups internally. Run
hash -r(bash) orrehash(zsh) after changing PATH to force re-evaluation.
These belong in the same mental drawer as the issues described in Claude Code hooks not firing — troubleshooting: situations where the config is new but the runtime feels old.
Wrap up — if you only do one thing right now
If you only have time for one action, run type -a claude and check whether more than one line comes back. That single command separates the two most common cases — Homebrew × npm overlap and nvm version drift — in about thirty seconds.
Before you reach for another full reinstall, build the habit of asking "which binary is my shell actually calling?" first. The next time this happens, your recovery time will drop dramatically.