Skip to main content

npm 12 allowScripts silently blocks native rebuilds

lesson · created 2026-09-18 · actor hermes · confidence high · status active tags: npm node native-modules better-sqlite3 pm2

Rule​

npm rebuild <native-pkg> can print rebuilt dependencies successfully and exit 0 while doing nothing, when npm 12 has the package's install script blocked. Declare the gate in the package that needs the native build:

"allowScripts": { "better-sqlite3@12.11.1": true }

npm install-scripts ls names the blocked packages. This npm version defaults to blocking install scripts for packages not covered by allowScripts, and npm rebuild reports success regardless.

Why it matters​

Verify the rebuild with the module itself, never with npm's exit code:

node -e "require('better-sqlite3')"   # throws ERR_DLOPEN_FAILED when still stale

An ABI mismatch otherwise survives a "successful" rebuild and the service keeps crash-looping at full tilt. Observed 2026-09-18: nexus-web (:55059, pm2 id 17) served HTTP 500 Cannot read properties of null (reading 'prepare') for a day after a rebuild that had silently no-opped, while the sibling tree nexus-equities/web rebuilt correctly only because its package.json already carried an allowScripts entry.

The node ABI pair in play here: stale module built for NODE_MODULE_VERSION 127, runtime node v24.21.0 reports 137. The error text is was compiled against a different Node.js version.

  • Crash-loop cost when this is left alone: eq-web reached 227,402 pm2 restarts (~4/sec) and a 283 MB error log at ~4.2 KB/s growth. Check pm2 describe <app> restart counts and wc -c ~/.pm2/logs/<app>-error.log before assuming a Node service is merely "online".
  • pm2 flush <app> after a fix; ~/.pm2/logs held 586 MB, 290 MB after the flush.

Sources: /Volumes/FATTY/src/nexus/package.json · /Volumes/FATTY/src/nexus-equities/web/package.json · npm 12.0.2 npm install-scripts ls output · session 2026-09-18 (cron)