Skip to content

[BUG] npm exec / npx forwards persistent user-level allow-scripts to children as npm_config_allow_scripts, failing project-scoped installs in the child (EALLOWSCRIPTS) #9968

Description

@techaboo

Summary

Setting allow-scripts in a user .npmrc (which npm's own warning recommends for global installs, and which is a valid config source for project installs) makes any tool launched via npx/npm exec that shells out to a project-scoped npm install fail with:

npm error code EALLOWSCRIPTS
npm error --allow-scripts is not allowed in project-scoped installs. Add the entries to the "allowScripts" field in package.json, or to .npmrc, instead.

npm exec exports the resolved npm config into the child process's environment as npm_config_* variables. Because the outer process read allow-scripts from a user .npmrc, the launched tool (and anything it spawns) inherits npm_config_allow_scripts, and when it reaches an inner project-scoped npm install, that inner npm resolves the value via its env source — which resolveAllowScripts groups with cli and rejects for non-global installs. The user never passed --allow-scripts; the value lives exactly where the error message tells them to put it.

This is the npm exec sibling of #9783 (git-dependency preparation) and #9912/#9913 (npm run-script lifecycle export). The #9913 fix keeps persistent allow-scripts out of lifecycle-exported config; the npm exec child env export still forwards it (verified on 12.0.2 below).

Environment

  • npm: 11.17.0 (full repro) and 12.0.2 (exec forwarding and EALLOWSCRIPTS both independently verified)
  • node: v24.19.0
  • OS: Windows 11 (Git Bash); the config/export mechanics are platform-independent

How the offending setting gets there

Same as #9783 / #9912 — the documented workflow. Installing a global package with an install script prints:

npm warn allow-scripts <pkg>@x.y.z (postinstall: node install.js)
npm warn allow-scripts Run `npm install -g --allow-scripts=<pkg>` to allow these scripts once, or `npm config set allow-scripts=<pkg> --location=user` to allow them for all global installs.

Following the second suggestion writes allow-scripts=<pkg> to the user .npmrc. From then on, every npx-launched tool that performs project-scoped npm installs fails.

Minimal, self-contained reproduction

Isolated --userconfig so the reporter's own .npmrc is not involved:

R=$(mktemp -d)
echo "allow-scripts=whatever" > "$R/userrc"   # exactly as `npm config set allow-scripts=... --location=user` writes it
mkdir "$R/app" && cd "$R/app"
echo '{"name":"app","private":true}' > package.json

# (A) Direct project-scoped install with the same user config — works
npm install is-even --userconfig "$R/userrc"

# (B) Same install reached through npx — fails
npx -y --userconfig "$R/userrc" -c 'npm install is-even'

Actual result (B)

npm error code EALLOWSCRIPTS
npm error --allow-scripts is not allowed in project-scoped installs. Add the entries to the "allowScripts" field in package.json, or to .npmrc, instead.

Export proof

npx -y --userconfig "$R/userrc" -c 'node -e "console.log(process.env.npm_config_allow_scripts)"'
# → whatever

On npm 12.0.2: npm exec still sets npm_config_allow_scripts in the child environment, and an inner install with that variable present fails with the same EALLOWSCRIPTS error.

Real-world impact

npx @robzolkos/lazypi — a community installer for the Pi coding agent that shells out to pi install, which runs npm install <pkg> --prefix <pi-dir> --legacy-peer-deps for each package — fails on all 23 npm-backed packages at once with this error on any machine whose user .npmrc carries the recommended allow-scripts entry. The same applies to any scaffolder/bootstrap tool driven via npx that runs nested project-scoped npm installs. Notably, running the identical installer from a global install (plain Node child, no npm exec env) works fine, which makes this particularly confusing to debug.

Expected result

The npx-launched tool's inner install completes. A persistent user-level allow-scripts policy is not a command-line flag; npm exec should not convert it into one by exporting it into the child environment.

Root cause (source pointers)

  • lib/utils/resolve-allow-scripts.js — policyFromSources(npm, ['cli', 'env']) groups the env source with cli; a non-global, non-skipProjectConfig install throws EALLOWSCRIPTS when that layer has a policy.
  • npm exec exports resolved config (including user .npmrc values such as allow-scripts) to the child process as npm_config_*. fix(config): avoid exporting persistent allow-scripts #9913 excluded allow-scripts from lifecycle exports; the exec path's export still includes it.

Suggested fixes

  1. (Preferred) Apply the fix(config): avoid exporting persistent allow-scripts #9913 treatment to the npm exec child environment: keep persistent allow-scripts policy out of exec-exported config, so nested npm commands reload it from its original config source instead of treating it as an environment override.
  2. Alternatively, strip npm_config_allow_scripts from the child env in npm exec — it is a one-off/global-context flag by design, so there is no valid exec-child use for it.
  3. At minimum, fix the error message for the env-inherited case: telling the user to "add the entries to .npmrc" sends them in a circle — the entry already is in .npmrc.

Related

Workaround

Tools/launchers: don't reach the tool through npm exec (a globally installed copy runs with a clean environment and works). End users: keep allow-scripts out of persistent user config and pass it per-invocation for global installs only (npm install -g <pkg> --allow-scripts=<pkg>), or move the policy into each project's own package.json allowScripts field.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions