[BUG] Interrupted global auto-update (macOS/npm) leaves broken half-install: orphaned staging symlink + missing native optional dep, no rollback → `claude: command not found`
Summary
A background auto-update of the global npm install on macOS was interrupted partway through and left the install in a broken half-state with no rollback: the claude command disappeared entirely (command not found), because (a) the final symlink rename never completed and (b) the platform-native optional dependency (@anthropic-ai/claude-code-darwin-arm64, ~241 MB) was never downloaded. The user had a working claude earlier the same day; the only thing that changed was the auto-update firing for the newly-published 2.1.207.
Related but distinct: #65093 (transient network loss → stuck "Auto-update failed" banner) — that one is Windows-native and about the banner state; this one is macOS global-npm and about a non-recoverable half-installed package.
Environment
- OS: macOS (Darwin 25.5.0), arm64
- Node: v26.0.0 · npm: 11.15.0
- Install method: global npm (
installMethod: global), prefix/opt/homebrew - Package:
@anthropic-ai/claude-code, updating to 2.1.207 (published 2026-07-10T22:25Z)
Symptom
$ claude
zsh: command not found: claude
which -a claude → nothing. Earlier the same day claude worked normally.
Broken state found on disk
- Orphaned staging symlink, no final
claude:/opt/homebrew/bin/contained only a temp npm staging link and noclaude:
````
lrwxr-xr-x .claude-WV2akNWU -> ../lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe
(i.e. npm's stage-then-rename never reached the rename step.)
- Native optional dependency missing:
@anthropic-ai/claude-code-darwin-arm64was not installed anywhere. After manually recreating theclaudesymlink, running it produced:
```
Error: claude native binary not installed.
Either postinstall did not run (--ignore-scripts, some pnpm configs)
or the platform-native optional dependency was not downloaded (--omit=optional).
cli-wrapper.cjs
The JS fallback () also failed for the same reason (Could not find native binary package "@anthropic-ai/claude-code-darwin-arm64"`).
Root cause (analysis)
Three design properties combine so that a single interrupted download leaves the CLI completely non-functional with no fallback:
- The real engine is a large "optional" dependency fetched at postinstall. The installed
claudeis a small launcher shim; the actual ~241 MB native binary is a separate optional dependency. In npm, optional-dependency download failures are silent — npm reports the install as succeeded even though the package cannot run. - The global swap is not atomic. npm stages new files under a temp name (
.claude-XXXXXXXX) and renames toclaudeas the last step. An interruption between staging and rename leaves an orphaned temp symlink and noclaudeat all — rather than leaving the previously-working version in place. - No rollback + unattended. The update runs in the background, so a transient failure (network drop mid-241 MB download, machine sleep, or the triggering terminal being closed) is neither retried nor surfaced; the previously-working install is already gone. The user only discovers it later as
command not found.
Reproduction (conceptual)
- Have a working global npm install of
claude-codeon macOS arm64. - Trigger the auto-update to a new version (or
npm install -g @anthropic-ai/claude-code@latest). - Interrupt it during the native-binary download / before the final symlink rename (kill the process, drop the network, or sleep the machine).
- Result:
claude: command not found, an orphaned/opt/homebrew/bin/.claude-*staging symlink, and a missing@anthropic-ai/claude-code-darwin-arm64. No automatic recovery on next shell.
Workaround that fixed it
npm install -g @anthropic-ai/claude-code --include=optional
(Recreating the claude symlink alone is not enough — the native optional dep must be re-fetched.)
Suggested fixes
- Make the optional-native-dep download a hard failure, or verify the native binary is present at the end of postinstall and fail the install loudly if it isn't, so npm doesn't report success on a non-runnable package.
- Atomic update / rollback: don't remove the previously-working version until the new one (including the native binary) is fully staged and verified; on failure, restore the prior version.
- Retry + surface transient download failures during unattended auto-update instead of leaving an orphaned staging symlink.
---
Filed from a Claude Code session after diagnosing and fixing the broken install.