[BUG] `npm ci` accepts a lockfile that violates `overrides` when a hoisted package is reached through multiple override scopes · Issue #10137 · npm/cli · GitHub
Skip to content

[BUG] npm ci accepts a lockfile that violates overrides when a hoisted package is reached through multiple override scopes #10137

Open

Description

@szegedi

[BUG] npm ci accepts a lockfile that violates overrides when a hoisted package is reached through multiple override scopes

Is there an existing issue for this?

  • I have searched the existing issues

This issue exists in the latest npm version

  • I am using the latest npm

Current Behavior

When the same hoisted package is reached both through a global override and through a path-scoped override, npm 11's npm ci accepts a lockfile that does not satisfy those overrides. It installs the non-overridden version without an error, and npm ls does not report the tree as invalid.

In the reproduction below, both overrides require brace-expansion@^5.0.12 under minimatch. The lockfile has brace-expansion@5.0.9 there. npm 11 installs 5.0.9:

$ npx npm@11 ci
added 25 packages, and audited 26 packages in 406ms

$ npm ls brace-expansion
npm-override-repro@1.0.0
└─┬ tshy@4.1.3
  └─┬ minimatch@10.2.6
    └── brace-expansion@5.0.9

npm 10 rejects the same package.json and lockfile:

$ npx npm@10 ci
npm error `npm ci` can only install packages when your package.json and package-lock.json or npm-shrinkwrap.json are in sync. Please update your lock file with `npm install` before continuing.
npm error
npm error Invalid: lock file's brace-expansion@5.0.9 does not satisfy brace-expansion@5.0.12

Running npm install or npm install --package-lock-only with npm 11 doesn't repair the lockfile either; it treats the lockfile as already in sync. Regenerating with npm 10 does fix it.

Since overrides are commonly used to force patched versions of transitive dependencies, this is quite significant for security. Here the override silently has no effect, and npm ci doesn't catch it. This looks like a regression compared to npm 10.

Expected Behavior

npm ci should fail with Invalid: lock file's brace-expansion@5.0.9 does not satisfy brace-expansion@5.0.12, as npm 10 does. npm install should update the lockfile so the override is satisfied.

Steps To Reproduce

Run this script. It creates a temporary project, generates a lockfile while the overrides pin 5.0.9, then tightens the overrides to ^5.0.12 without updating the lockfile:

#!/usr/bin/env bash
set -euo pipefail
dir=$(mktemp -d); cd "$dir"

write_pkg() {
  cat > package.json <<JSON
{
  "name": "npm-override-repro",
  "version": "1.0.0",
  "private": true,
  "devDependencies": { "tshy": "4.1.3" },
  "overrides": {
    "minimatch": { "brace-expansion": "$1" },
    "tshy": { "sync-content": { "glob": { "minimatch": { "brace-expansion": "$1" } } } }
  }
}
JSON
}

# 1. Generate a lockfile while the overrides pin brace-expansion@5.0.9.
write_pkg "5.0.9"
npx -y npm@11 install --package-lock-only --ignore-scripts --silent

# 2. Tighten the overrides to ^5.0.12 without updating the lockfile.
write_pkg "^5.0.12"

for v in 10 11; do
  rm -rf node_modules
  echo "=== npm@$v ci"
  npx -y npm@$v ci --ignore-scripts 2>&1 | grep -E 'Invalid|Missing|added' || true
done
echo "=== installed tree (after npm@11 ci)"
npm ls brace-expansion || true

Output:

=== npm@10 ci
npm error Invalid: lock file's brace-expansion@5.0.9 does not satisfy brace-expansion@5.0.12
=== npm@11 ci
added 25 packages, and audited 26 packages in 406ms
=== installed tree (after npm@11 ci)
npm-override-repro@1.0.0
└─┬ tshy@4.1.3
  └─┬ minimatch@10.2.6
    └── brace-expansion@5.0.9

The relevant structure is that tshy depends on sync-content, which depends on glob@13, which depends on minimatch@^10. That minimatch is hoisted to node_modules/minimatch and uses the hoisted node_modules/brace-expansion@5.0.9. So the same minimatch node is reached through both override scopes.

We hit this in a real project after an automated dependency update tool, which uses npm 11, generated overrides like these. The resulting lockfile passed locally with npm 11 but failed in CI on Node 18/20/22, which ships with npm 10.

Environment

  • npm: 11.16.0 (accepts), 10.9.9 (rejects)
  • Node.js: v24.18.0
  • OS: macOS (Darwin 25.6.0); the same npm 10 failure also occurs on GitHub Actions ubuntu-latest
  • npm config: defaults

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

    Priority 2secondary priority issueconfig:overridesIssues dealing with the overrides feature

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions