Auto-update silently installs truncated native binary on darwin-arm64 when laptop sleeps mid-download (v2.1.138)
Drafted by Claude Code itself during a live debugging session. I (the OP) reviewed and approved the wording before filing — flagging up front for transparency since the entire diagnosis, manual fix, and writeup below are AI-authored.
Environment
- macOS Darwin 25.4.0 (Apple Silicon, arm64)
- Homebrew npm prefix:
/opt/homebrew/lib/node_modules - Claude Code installed globally:
@anthropic-ai/claude-codev2.1.138
Symptom
After an auto-update fired in the background while the laptop was sleeping, new claude launches die immediately:
~ ❯ claude
zsh: killed claude
Already-running sessions (pre-update) keep working — only fresh launches are affected.
Root cause (diagnosed)
The native binary on disk is truncated to ~20% of its actual size. A fresh download of the same package from npm shows the gap:
| | Truncated install (broken) | Fresh npm pack @anthropic-ai/claude-code-darwin-arm64@2.1.138 |
|---|---|---|
| Size | 41,287,168 bytes (~41 MB) | 205,062,416 bytes (~205 MB) |
| Code signature | "code object is not signed at all" | Valid (ad-hoc-able) |
| codesign --force --sign - | Fails: "main executable failed strict validation" | Succeeds |
| Launch | SIGKILL (zsh: killed) | Normal |
The Mach-O header parses successfully because the truncation is in the body — but load commands reference data past byte 41M that doesn't exist on disk, so the kernel kills the process on first access.
This propagated through the install in three observable downstream ways:
bin/claude.exewas still the ASCII postinstall stub (postinstall never reached the binary-copy step)node_modules/@anthropic-ai/claude-code-darwin-arm64/package.jsonwas missing- Even after manually putting things in place,
codesign --force --sign -rejected the binary because the truncated Mach-O failed strict validation
Likely trigger
Sleep mid-download during a background auto-update. The corrupted file's mtime was a midday timestamp consistent with the laptop sleep-cycling. macOS suspending Wi-Fi tears down the TCP stream from the npm CDN; npm's tarball extractor sees the stream end, treats it as "download complete," writes the partial file, and moves on without throwing.
Reproduction sketch
- Have
clauderunning on darwin-arm64 with auto-update enabled - While
claudeis checking/downloading an update in the background, sleep the laptop - Wake later, try
claudein a new terminal - Observe
zsh: killed, withbin/claude.exebeing either the postinstall stub or a sub-100 MB Mach-O that failscodesign -v
Manual fix that worked
The cascading symptoms required walking through three patches before getting to the actual root cause (the source binary being itself corrupt required a fresh download):
cd /tmp && npm pack @anthropic-ai/claude-code-darwin-arm64@2.1.138
tar -xzf anthropic-ai-claude-code-darwin-arm64-2.1.138.tgz
PKG=/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code
cp package/claude "$PKG/node_modules/@anthropic-ai/claude-code-darwin-arm64/claude"
cp package/package.json "$PKG/node_modules/@anthropic-ai/claude-code-darwin-arm64/package.json"
cp package/claude "$PKG/bin/claude.exe"
chmod +x "$PKG/bin/claude.exe"
codesign --force --sign - "$PKG/bin/claude.exe"
Suggested fixes
- Verify download integrity before treating extract as complete. npm's registry metadata includes an integrity hash for the tarball; the auto-update flow should compare extracted size/hash against the registry's metadata before claiming success.
- Verify destination binary at end of
install.cjs. A simplecodesign -vcheck (or a size sanity check vs.package.json's declared size) on darwin would catch truncation and either re-download or bail loudly instead of leaving a silently-broken install. - Make auto-update sleep-resilient. Either pause/resume on sleep/wake events, or treat any TCP disconnect during download as a hard failure that triggers re-download rather than accepting the partial extract.
Related
- #52746 — same SIGKILL-on-launch symptom but different root cause (signature stripped during copy of a complete binary, not source binary truncated). Together these two suggest
install.cjshas no integrity check at any stage of the install pipeline.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗