Skip to content
Nilay Kabariya

Entry No.039·Tested·Updated ·5 min read

npm 12 breaking changes, tested: what actually breaks

npm 12 blocks install scripts by default, but most packages still work. I installed 11 popular ones to find which break, plus 4 new error codes and the fixes.

by Nilay#npm#node#windowsTESTED

Verdict

The headline change, install scripts blocked by default, broke puppeteer, cypress, sqlite3 and Claude Code's npm package, and silently skipped lefthook's git hooks. esbuild, bcrypt, sharp and better-sqlite3 kept working. Every install reported success, and the only sign was a warning after the summary.

Tested on

npm
12.2.0 (also 12.0.0 / 11.19.1 for comparison)
Node
26.10.0 / 24.14.0
OS
Windows 11 Pro
Contents
  1. 1. Install scripts are blocked by default01
  2. 2. Git and tarball dependencies are refused02
  3. 3. Unknown flags now throw, but unknown .npmrc settings don’t (yet)03
  4. 4. Smaller changes that bit in testing04
  5. Should you upgrade?05
  6. How this was tested06

npm 12 is the current latest, so npm install -g npm now lands on it. Node itself hasn’t moved: even Node 26.10.0 still ships npm 11.19.1, so you only get 12 by upgrading npm yourself, or when a CI image does it for you. I took npm 12.2.0, installed real packages into fresh projects, and used each one afterwards. That last step matters, because npm 12 reports success either way.

1. Install scripts are blocked by default

The big change. Packages can no longer run preinstall, install or postinstall scripts unless your project allows them. The install finishes, prints “found 0 vulnerabilities”, and only then:

npm warn install-scripts 5 packages had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts   @sentry/cli@3.8.0 (postinstall: node ./scripts/install.js)
npm warn install-scripts   cypress@16.1.1 (postinstall: node dist/index.js --exec install)
npm warn install-scripts   lefthook@2.1.16 (postinstall: node postinstall.js)
npm warn install-scripts   puppeteer@25.12.0 (postinstall: node install.mjs)
npm warn install-scripts   sqlite3@6.0.1 (install: prebuild-install -r napi || node-gyp rebuild)

A blocked script doesn’t automatically mean a broken package. Many ship a prebuilt binary and use the script only as a check. So I used every package after installing it:

Package Script blocked? Works after install? What you see
esbuild 0.28.2 Yes ✅ Yes transformed TypeScript fine
bcrypt 6.0.0 Yes ✅ Yes hashed fine (ships prebuilt binaries)
sharp No script ✅ Yes made a PNG
better-sqlite3 13.0.3 No script ✅ Yes ran a query
Sentry CLI 3.8.0 Yes ✅ Yes sentry-cli 3.8.0
puppeteer 25.12.0 Yes ❌ No Could not find Chrome (ver. 154.0.8037.57) (fix)
cypress 16.1.1 Yes ❌ No No version of Cypress is installed in: …
sqlite3 6.0.1 Yes ❌ No Could not locate the bindings file (fix)
lefthook 2.1.16 Yes ⚠️ Silently no git hooks installed, no error
Claude Code, npm global install Yes ❌ No claude.exe is not compatible with the version of Windows

The pattern: packages whose script downloads or builds something essential break (a browser, an app binary, a native module). Packages that only verify a prebuilt binary keep working. lefthook is the nasty one: it installs fine and runs fine, and your pre-commit hooks just never get set up.

Allow the packages that need their scripts, then run them:

npm install-scripts approve puppeteer sqlite3 lefthook
npm rebuild

approve writes an allowScripts block into package.json, which you commit so teammates and CI get the same answer:

"allowScripts": {
  "sqlite3@6.0.1": true,
  "lefthook@2.1.16": true,
  "puppeteer@25.12.0": true
}

To approve by name instead, so upgrades keep working:

npm install-scripts approve lefthook --no-allow-scripts-pin

That writes "lefthook": true, and switching versions after that produced no warning.

For packages whose script you’ve decided you don’t need, npm install-scripts deny <pkg> records false and stops the warning. npm install-scripts ls shows what’s still undecided.

Global installs work differently: there’s no project package.json to hold approvals. npm’s own warning gives the two options, and I tested both with Claude Code:

npm install -g @anthropic-ai/claude-code --allow-scripts=@anthropic-ai/claude-code
npm config set allow-scripts=@anthropic-ai/claude-code --location=user

The full story of that failure: Claude Code “not compatible with the version of Windows”.

2. Git and tarball dependencies are refused

Dependencies fetched from git or from a tarball URL are now off by default. These do fail the install, with new error codes:

npm error code EALLOWGIT
npm error Fetching packages of type "git" have been disabled
npm error Refusing to fetch "github:jonschlinkert/is-odd"
npm error code EALLOWREMOTE
npm error Fetching packages of type "remote" have been disabled
npm error Refusing to fetch "https://registry.npmjs.org/is-odd/-/is-odd-3.0.1.tgz"

Allow them for your own package.json (root), in the project’s .npmrc:

allow-git=root
allow-remote=root

The release notes also allow all. root is the narrower of the two, so start there.

3. Unknown flags now throw, but unknown .npmrc settings don’t (yet)

The release notes say unknown configs in .npmrc now throw. In my tests they don’t, on 12.0.0 or 12.2.0. A pnpm setting left in .npmrc still only warns:

npm warn Unknown project config "auto-install-peers". This will stop working in the next major version of npm.

Same for strict-peer-dependencies and shamefully-hoist. A user-level .npmrc warned too, and npm 11 prints the identical warning. Fix these now anyway: the warning says they’ll stop working in the next major version.

What does throw now is an abbreviated command-line flag, which older npm versions expanded for you:

npm install is-odd --leg
npm error code EUNKNOWNCONFIG
npm error Unknown cli flag:
npm error   - --leg

Write flags in full: --legacy-peer-deps.

4. Smaller changes that bit in testing

Change What happened on npm 12.2.0
npm shrinkwrap removed Unknown command: "shrinkwrap". Rename npm-shrinkwrap.json to package-lock.json
npm adduser removed Unknown command: "adduser". Use npm login
npm star removed Surprise: npm star is-odd now runs as npm start, giving Missing script: "start" (that error explained)
npm view --json Always an array now: [ "3.0.1" ] where npm 11.9.0 printed "3.0.1". Scripts that parse it break
npm init -y No license field at all (was "ISC")
Node support ^22.22.2 || ^24.15.0 || >=26.0.0. On Node 24.14.0 npm 12 printed npm v12.2.0 does not support Node.js v24.14.0 but still ran

Should you upgrade?

Yes, but plan for it. Before upgrading npm, on your current version, run npm install and read the install-scripts warning (recent npm 11 releases already list the packages, worded “not yet covered by allowScripts”). That list is everything npm 12 will block. Check each one against the table above, approve the ones that download or build something, and commit the allowScripts block. Then upgrade.

How this was tested

npm 12.2.0 installed into a separate folder and run on a portable Node 26.10.0 (checksum-verified), with 12.0.0 and the bundled 11.19.1 for comparison. Each package was installed into a fresh project and then actually used: a transform, a hash, an image, a query, a browser launch, cypress verify, a version check. Download caches for puppeteer and Cypress were pointed at empty folders so nothing cached from earlier could hide a failure. Every message above is the real output.

— N.K., end of entry No.039

Useful? Pass it on:Post on XFollow @EmotionalMatter

Related entries

  1. No.042

    Fix: "Could not locate the bindings file" in sqlite3

    sqlite3 or better-sqlite3 installed fine, then can't find its .node file. Four causes reproduced, how the Tried: paths tell them apart, and the fix for each.

    > Error: Could not locate the bindings file. Tried:

    FIXED3 min
  2. No.036

    Fix: npm error Missing script: "dev" (and "start")

    npm can't find the script you asked for. Six real causes, from a stray package.json up the tree to monorepos, and two commands that show which one you have.

    > npm error Missing script: "dev"

    FIXED5 min
  3. No.017

    Fix: 'vite' is not recognized as an internal or external command

    Windows says the command doesn't exist, but it's right there in node_modules. The four causes, how to tell them apart in one command, and what each one needs.

    > 'vite' is not recognized as an internal or external command,

    FIXED4 min

Post card · Newsletter

Get the next fix in your inbox.

One short email when a new entry is published. No spam, never shared, and you can leave any time.

— Nilay

or follow by RSSor on X

By subscribing you agree to the privacy note. One click to leave.

tip: paste the exact error text