Skip to content

[Bug]: Provider update leaves a broken claude install on npm 12 and reports success #5627

Description

@hey-jj

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Install @anthropic-ai/claude-code globally with npm 12 under nvm and confirm claude runs.
  2. In the desktop app, click the update control for the claude provider.
  3. Wait for the app to report the update as successful.
  4. Run claude in a terminal.

Expected behavior

The app reports the update as successful, and every subsequent claude invocation fails with:

Error: claude native binary not installed.

I reproduced this twice, on different days, with the same result.

The update command comes from makeNpmGlobalProviderMaintenanceCapabilities in apps/server/src/provider/providerMaintenance.ts and runs through updateProvider in providerMaintenanceRunner.ts:

npm install -g @anthropic-ai/claude-code@latest

npm 12 blocks install scripts unless the package is listed in the allow-scripts config, which is empty by default. A blocked script produces only a warning and the install still exits 0. The package's postinstall (install.cjs) is what copies the platform-native binary over the bin/claude stub. The platform package downloads, the blocked script never runs, the stub stays in place, and npm removes the previously working tree while applying the change. The install ends broken with an exit code of 0. From the npm debug log of the actual update run:

6 verbose title npm install @anthropic-ai/claude-code@latest
7 verbose argv "install" "--global" "@anthropic-ai/claude-code@latest"
71 silly CHANGE node_modules/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code-darwin-arm64
72 warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
72 warn install-scripts   @anthropic-ai/claude-code@2.1.224 (postinstall: node install.cjs)
77 verbose exit 0

updateProvider treats a zero exit as success, so the user sees a successful update while claude is broken.

A one-line change in makeNpmGlobalProviderMaintenanceCapabilities fixes this and scopes the allowance to exactly the package being updated:

updateArgs: [
  "install",
  "-g",
  `--allow-scripts=${definition.npmPackageName}`,
  `${definition.npmPackageName}@latest`,
],

This is what npm's own warning prescribes. npm 11 already ships allow-scripts as a known config key and accepts the flag without complaint. I verified this on 11.17.0. Older npm treats it as unknown config, warns, and continues with exit 0. I verified that path on 11.17.0 with a deliberately unknown flag, and warn-and-continue is npm's long-standing behavior for unknown config.

The pnpm-global and bun-global update paths in the same file are likely exposed to the same class of failure, since pnpm 10 and newer and bun ship their own script-blocking defaults. I did not reproduce those. Separately, because the runner accepts any zero exit, every blocked-postinstall update reports success while the CLI is broken. Scanning stderr for the warn install-scripts marker would catch this class regardless of package manager.

Actual behavior

The app reports the update as successful, and every subsequent claude invocation fails with:

Error: claude native binary not installed.

I reproduced this twice, on different days, with the same result.

The update command comes from makeNpmGlobalProviderMaintenanceCapabilities in apps/server/src/provider/providerMaintenance.ts and runs through updateProvider in providerMaintenanceRunner.ts:

npm install -g @anthropic-ai/claude-code@latest

npm 12 blocks install scripts unless the package is listed in the allow-scripts config, which is empty by default. A blocked script produces only a warning and the install still exits 0. The package's postinstall (install.cjs) is what copies the platform-native binary over the bin/claude stub. The platform package downloads, the blocked script never runs, the stub stays in place, and npm removes the previously working tree while applying the change. The install ends broken with an exit code of 0. From the npm debug log of the actual update run:

6 verbose title npm install @anthropic-ai/claude-code@latest
7 verbose argv "install" "--global" "@anthropic-ai/claude-code@latest"
71 silly CHANGE node_modules/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code-darwin-arm64
72 warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
72 warn install-scripts   @anthropic-ai/claude-code@2.1.224 (postinstall: node install.cjs)
77 verbose exit 0

updateProvider treats a zero exit as success, so the user sees a successful update while claude is broken.

A one-line change in makeNpmGlobalProviderMaintenanceCapabilities fixes this and scopes the allowance to exactly the package being updated:

updateArgs: [
  "install",
  "-g",
  `--allow-scripts=${definition.npmPackageName}`,
  `${definition.npmPackageName}@latest`,
],

This is what npm's own warning prescribes. npm 11 already ships allow-scripts as a known config key and accepts the flag without complaint. I verified this on 11.17.0. Older npm treats it as unknown config, warns, and continues with exit 0. I verified that path on 11.17.0 with a deliberately unknown flag, and warn-and-continue is npm's long-standing behavior for unknown config.

The pnpm-global and bun-global update paths in the same file are likely exposed to the same class of failure, since pnpm 10 and newer and bun ship their own script-blocking defaults. I did not reproduce those. Separately, because the runner accepts any zero exit, every blocked-postinstall update reports success while the CLI is broken. Scanning stderr for the warn install-scripts marker would catch this class regardless of package manager.

Impact

Major degradation or frequent failure

Version or commit

main @ 5661c61

Environment

macOS arm64 (Darwin 25), Node v24.18.1, npm 12.0.2, @anthropic-ai/claude-code 2.1.224 installed npm-global under nvm

Logs or stack traces

6 verbose title npm install @anthropic-ai/claude-code@latest
7 verbose argv "install" "--global" "@anthropic-ai/claude-code@latest"
71 silly CHANGE node_modules/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code-darwin-arm64
72 warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
72 warn install-scripts   @anthropic-ai/claude-code@2.1.224 (postinstall: node install.cjs)
77 verbose exit 0

Screenshots, recordings, or supporting files

No response

Workaround

Run the skipped postinstall by hand:

node <global node_modules>/@anthropic-ai/claude-code/install.cjs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.needs-triageIssue needs maintainer review and initial categorization.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions