[BUG] Auto-update silently exits 0 when native optional dependency download is throttled — leaves stub binary + no bin symlink → "command not found"
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
On a slow/throttled connection to registry.npmjs.org, npm install @anthropic-ai/claude-code@latest
(what the auto-updater runs) fails to download the large platform-native optional dependency,
yet exits 0 reporting success. The result is a broken install: bin/claude.exe is left as the
500-byte fallback stub, and the bin/claude symlink is never created — so the next launch fails withclaude: command not found. The user gets no error at update time; the update "succeeds" silently.
This is distinct from the Rosetta/arch case (#50974) and the Bun case (#50203): here the correct
platform package IS resolved, it just never finishes downloading, and because it's an optional
dependency npm drops it without failing the install.
What Should Happen?
When the native binary can't be obtained, the install/update should fail loudly (non-zero exit),
or the updater should verify bin/claude.exe is a real executable before declaring success —
never leave a silently-broken install that only surfaces at next launch.
Error Messages/Logs
77 silly tarball no local data for @anthropic-ai/claude-code-linux-x64@...tgz. Extracting by manifest.
78 verbose reify failed optional dependency .../@anthropic-ai/claude-code-linux-x64
79 silly reify mark deleted [ '.../@anthropic-ai/claude-code-linux-x64' ]
80 info run @anthropic-ai/claude-code@2.1.197 postinstall ... node install.cjs
95 verbose exit 0
96 info ok
Resulting state: `file bin/claude.exe` -> `ASCII text` (the stub, not an ELF); `~/.npm-global/bin/claude` missing.
Steps to Reproduce
The underlying mechanism is generic npm behavior (optional-dependency tarball fetch times out →
npm silently drops it and still exits 0), combined with install.cjs never validating that the
binary it wrote is real. Part A reproduces the npm mechanism deterministically in under a minute
with no special network conditions required. Part B is what actually happened with claude-code.
Part A — minimal, deterministic repro of the npm mechanism (no slow network needed)
- Create a tiny "slow" package and serve it locally with an artificial delay:
``bash``
mkdir -p /tmp/repro/pkg && cd /tmp/repro/pkg
npm init -y -s >/dev/null && echo "module.exports = 1;" > index.js
npm pack >/dev/null # -> pkg-1.0.0.tgz
cd /tmp/repro
python3 -c "
import http.server, socketserver, time
class SlowHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path.endswith('.tgz'):
time.sleep(5) # simulate a stalled/throttled download
return super().do_GET()
with socketserver.TCPServer(('127.0.0.1', 8899), SlowHandler) as httpd:
httpd.serve_forever()
" &
- Create a consumer package that depends on it as an optional dependency via a direct tarball URL:
``bash``
mkdir -p /tmp/repro/consumer && cd /tmp/repro/consumer
cat > package.json <<'EOF'
{
"name": "consumer",
"version": "1.0.0",
"optionalDependencies": {
"slow-pkg": "http://127.0.0.1:8899/pkg/pkg-1.0.0.tgz"
}
}
EOF
- Force a short fetch timeout so the 5s artificial delay triggers the same failure npm hits on a
throttled real connection (on a genuinely slow link this happens naturally at the default timeout,
~5 min, without needing this flag):
``bash``
npm install --fetch-timeout=2000
echo "exit code: $?"
- Observe:
exit code: 0, butnode_modules/slow-pkgdoes not exist — npm silently dropped the
optional dependency and reported success anyway. This is the exact mechanism hitting
@anthropic-ai/claude-code-linux-x64 in Part B.
Part B — what actually happened with claude-code (2.1.197)
- On a machine with a normal-looking but throttled path to
registry.npmjs.orgfor large payloads
(small metadata requests fine, ~35 KB/s for large tarballs — confirmed via curl -w '%{speed_download}'),
let the built-in auto-updater run, or trigger it manually:
``bash``
npm install -g @anthropic-ai/claude-code@latest
echo "exit code: $?"
- Watch
~/.npm/_logs/*-debug-0.logfor the latest run — see the "Evidence" block below; note it
ends in verbose exit 0 / info ok despite the optional native package being marked deleted.
- Inspect the resulting binary — it's the fallback stub, not the real executable:
``bash``
file ~/.npm-global/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe
# -> ASCII text (expected: ELF 64-bit LSB executable ...)
ls -la ~/.npm-global/bin/claude
# -> No such file or directory (symlink was never (re)created)
- Launch the CLI:
``bash``
claude --version
# -> claude: command not found
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.197
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
WSL (Windows Subsystem for Linux)