[BUG] Linux daemon self-update leaves claude.exe as npm stub when install scripts are blocked

Status Open
Reported on v2.1.217
Maintainer reply None cached
Activity 1 comment · opened Jul 22, 2026

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?

Claude Code's background/daemon self-update path can repeatedly replace the daemon executable with the npm package's 500-byte "native binary not installed" stub when npm blocks package lifecycle scripts.

This is still reproducible with Claude Code 2.1.217 on Linux. It is the same class as the stale, closed anthropics/claude-code#62684, but with fresh Linux evidence and npm 12's allow-scripts policy rather than only macOS ignore-scripts=true.

Observed sequence:

  1. A running Claude Code background supervisor noticed /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe changed and attempted to self-restart.
  2. The self-respawn failed with ENOEXEC because that path was an ASCII stub, not the native ELF binary.
  3. The platform-specific optional package was present at node_modules/@anthropic-ai/claude-code-linux-x64/claude; only postinstall had been blocked, so the native binary was never linked/copied over bin/claude.exe.
  4. The same failure repeated across three consecutive updates/repairs: 2.1.215, 2.1.216, and 2.1.217.

The root cause in the npm logs is that the root/global npm install blocked the package script:

warn install-scripts @anthropic-ai/claude-code@2.1.217 (postinstall: node install.cjs)
warn install-scripts Run `npm install -g --allow-scripts=@anthropic-ai/claude-code` to allow these scripts once, or `npm config set allow-scripts=@anthropic-ai/claude-code --location=user` to allow them for all global installs.

After that install, bin/claude.exe was the stub. The daemon then tried to posix_spawn it and failed before the stub could print its own diagnostic.

The local workaround was:

sudo npm config set allow-scripts=@anthropic-ai/claude-code --location=user
sudo npm install -g @anthropic-ai/claude-code@2.1.217 --include=optional --foreground-scripts

The control reinstall then printed:

npm notice run @anthropic-ai/claude-code@2.1.217 postinstall
npm notice run node install.cjs

and /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe remained a Linux x64 ELF hardlink to the optional package binary. The daemon recovered immediately.

There was also a separate, noisy user-local failure: claude install latest attempted an npm-global install into the user's prefix and failed with EEXIST on an existing native launcher symlink. That wrote a stale .last-update-result.json warning, but the daemon-breaking executable was the root/global /usr stub described above.

What Should Happen?

Claude Code should never leave the executable path watched/spawned by the daemon as the npm stub after a background update attempt.

At minimum, one of these should be true:

  • the internal updater invokes npm with --allow-scripts=@anthropic-ai/claude-code or otherwise ensures install.cjs runs for this package;
  • if lifecycle scripts are blocked, the updater aborts before replacing the active executable path;
  • if the native package is already present, recovery runs install.cjs directly instead of using npm install -g;
  • the daemon's mtime-triggered self-restart verifies that the target is an actual native executable before spawning it;
  • the npm stub has a shebang/executable failure path so subprocess users get the intended remediation text instead of raw ENOEXEC.

The important invariant is: a failed or policy-blocked update must not turn a previously working Claude Code daemon into a broken install.

Error Messages/Logs

[supervisor] binary at /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe changed (mtime changed) -- self-restarting for upgrade
[supervisor] upgrade self-respawn failed to spawn: ENOEXEC: unknown error, posix_spawn '/usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe' -- bg workers may be orphan-reaped ~60s after this process exits unless a client restarts the daemon (run `claude agents`)

Before repair:

$ file /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe
/usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe: ASCII text

$ /usr/bin/claude --version
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).

The optional native binary was present:

$ ls -l /usr/lib/node_modules/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code-linux-x64/claude
-rwxr-xr-x 1 root root 268573680 ... claude

After setting root npm allow-scripts and reinstalling the same version:

npm notice run @anthropic-ai/claude-code@2.1.217 postinstall
npm notice run node install.cjs

$ file /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe
/usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe: ELF 64-bit LSB executable, x86-64, dynamically linked

$ claude daemon status
pid:     <pid>
version: 2.1.217
control.sock: reachable

Steps to Reproduce

One minimal reproduction for the broken package state:

  1. On Linux x64 with npm 12, use a root/global npm policy that blocks package scripts for @anthropic-ai/claude-code.
  2. Install or update the npm package under /usr:

``sh
sudo npm install -g @anthropic-ai/claude-code@2.1.217
``

  1. Observe npm warning that @anthropic-ai/claude-code postinstall: node install.cjs was blocked.
  2. Confirm the optional package exists but the public executable path is still the stub:

``sh
file /usr/lib/node_modules/@anthropic-ai/claude-code/bin/claude.exe
ls -l /usr/lib/node_modules/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code-linux-x64/claude
``

  1. Run /usr/bin/claude --version or let a running daemon self-restart after it notices the mtime change.
  2. The direct CLI prints the stub diagnostic; the daemon self-spawn path fails as raw ENOEXEC and leaves background service control unreachable until manual repair/restart.

This is especially damaging when triggered by Claude Code's own background updater, because it breaks an already-running install.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

Unknown. Reproduced across 2.1.215, 2.1.216, and 2.1.217 update attempts.

Claude Code Version

2.1.217 (Claude Code)

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

Related issues:

  • anthropics/claude-code#62684 reported the same self-update/stub loop on macOS with ignore-scripts=true, but it was closed as stale.
  • anthropics/claude-code#61552 covers the same stub surfacing as ENOEXEC because the stub has no shebang, but not the daemon self-update loop.
  • anthropics/claude-code#77384 covers a Linux daemon self-respawn failure during npm postinstall too, but with EACCES from a write/chmod race; this report is the ENOEXEC variant where lifecycle policy prevents postinstall from replacing the stub at all.
  • anthropics/claude-code#72193 mentions npm allow-scripts as a separate install issue, but its main report is a Windows silent-exit problem.

This report is specifically about preserving the daemon executable invariant during internal update/recovery: if npm policy blocks lifecycle scripts, Claude Code should not replace a working watched executable with the stub and then immediately try to spawn it.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗