[BUG] ignore-scripts=true + auto-update interaction crashes running Claude Code session; native binary repeatedly replaced with error stub on every self-update

Resolved 💬 2 comments Opened May 27, 2026 by mclements941-del Closed Jun 28, 2026

Environment

  • macOS arm64
  • @anthropic-ai/claude-code installed via Homebrew (symlink at /opt/homebrew/bin/claude → npm package at /opt/homebrew/lib/node_modules/...)
  • ~/.npmrc has ignore-scripts=true set globally as a baseline security posture against postinstall-based supply-chain attacks
  • Auto-update enabled (the Claude Code default)

What happens

In a single ~6-hour working session today, Claude Code's background self-update fired three separate times as new 2.1.x patch versions shipped. Each self-update reran the npm install under my ignore-scripts=true posture, the postinstall (install.cjs) was suppressed, and claude.exe was left as the 500-byte error-stub instead of being replaced with the real native binary fetched from the platform-specific optional dependency.

Symptoms in order:

  1. The running interactive Claude Code session crashed and dumped me back to my terminal home screen — not just a command exiting nonzero, the running process itself was killed mid-conversation.
  2. Attempting to restart with the claude command printed the familiar Error: claude native binary not installed. Either postinstall did not run (--ignore-scripts...) ... message.
  3. Running the Claude Code VS Code extension restored the binary after a few seconds (it ran something that the UI described as "doing the binaries" — almost certainly install.cjs executing).
  4. Restarting the terminal and rerunning claude worked again.

Each occurrence also killed an in-flight background workflow my session was orchestrating — three of the four times, the background bash subprocess running claude --print ... died along with the parent and the workflow lost state. The one survival case happened during a brief drafter-to-reviewer handoff window where the auto-update timing didn't intersect a live subprocess.

Why I think this is distinct from existing issues

  • #50609 ("claude native binary not installed") names --ignore-scripts as a known root cause but is filed against the initial install path on Windows. It doesn't address the auto-update interaction or the in-session-crash symptom.
  • #51266 ("vendor/ripgrep not created by install.cjs when ignore-scripts=true") covers a related ignore-scripts gap but is filed against the Grep tool's vendored ripgrep, not the claude binary itself, and was closed as duplicate.
  • #57178 ("Auto-update creates broken claude.exe symlink on macOS") covers auto-update breaking the install on macOS, but the failure mode there is a platform-detection bug pointing the symlink at a Windows binary that doesn't exist. My case is different: claude.exe does exist at the expected path, it's just the 500-byte error stub because the postinstall that would have replaced it was suppressed.

None of these capture "user with ignore-scripts=true loses their running session to a background self-update."

Workaround I'm using (works, but is user-side)

A PATH-shim wrapper that detects the stub by file size and runs install.cjs before exec'ing the real binary. Placed in ~/.local/bin/claude ahead of the Homebrew bin in PATH so both bash and zsh subshells route through it.

#!/bin/bash
# auto-healing wrapper around @anthropic-ai/claude-code
set -e
pkg_dir="/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code"
bin="${pkg_dir}/bin/claude.exe"
# install.cjs uses <4096 bytes as the stub-vs-real-binary threshold
if [[ ! -f "$bin" ]] || (( $(stat -f %z "$bin" 2>/dev/null || echo 0) < 4096 )); then
  echo "[claude] native binary missing — running postinstall..." >&2
  node "${pkg_dir}/install.cjs" >&2
fi
exec "$bin" "$@"

Adjust pkg_dir for non-Homebrew installs. Linux uses stat -c %s instead of stat -f %z. Note that a claude function in ~/.zshrc doesn't cover this case — bash subshells spawned by automation never source ~/.zshrc, so a PATH-shim is what reaches both shells.

Suggested upstream fixes

  1. Have install.cjs write the real binary to a different path than the stub, then write a tiny launcher script as the stub-replacement that always exec's the real binary by absolute path. The current architecture silently misroutes when the postinstall is suppressed; making the failure mode "the launcher script is missing" instead would be louder and more obviously broken.
  2. Consider whether background self-update should suspend itself while a parent Claude Code process holds a child claude invocation. As designed, the self-update tears the running binary out from under live child processes, which is the source of the in-session crashes — even users without ignore-scripts=true may be vulnerable to a similar race if the npm install momentarily writes the new binary in a non-atomic way.
  3. Ship a Homebrew formula (or other non-npm install path) so users who want both a security posture of ignore-scripts=true and a working claude don't have to maintain a user-side wrapper.

Happy to provide repro logs or test patches.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗