[BUG] `npm ci` fails because `npm install` produces an out-of-sync lockfile (regression since v7.0.9)
This issue exists in the latest npm version.
Summary
npm ci intermittently refuses to install because the lockfile generated by a prior npm install is not considered “in‑sync”. Rerunning npm install once (or more) “fixes” the lockfile, after which npm ci succeeds. This undermines deterministic installs and breaks CI for projects that should be reproducible.
This is a direct follow‑up to #6787, opened 2023‑09 and labeled Priority 1. It remains open (and dead) more than two years later. The problem persists even in the latest version v11 of npm.
Maintainer attention: the current assignee of the old issue has not responded on the thread for a long time. Multiple users have asked for reassignment. Please consider re‑triaging and reassigning so this Priority 1 bug can move forward.
Why this matters
- Breaks reproducible builds: older commits cannot be rebuilt reliably without mutating the lockfile.
- Causes flaky CI, especially when a dependency publishes a new compatible version between runs.
- Affects automation (e.g., Renovate) and monorepos/workspaces where lock determinism is essential.
Regression History / Technical Context
Community investigation in #6787 points to behavior changes beginning in v7.0.9 (first release where npm ci ignored locked versions), and further changes in 8.6.0, both touching installer logic. Reverting the implicated changes reportedly restores expected ci semantics up through later versions. Relevant commits discussed:
- v7.0.9 change:
0e58e6f(first observed wherenpm cimay install newer than locked) - v8.6.0 change:
bd96ae4(tightens checks, still leads tocifailure)
Known Workarounds (insufficient)
- Run
npm installtwice beforenpm ci. - Add specific
overridesto coerce versions? - Pin semver ranges more strictly than desired.
- Switch to an alternative package manager (e.g., pnpm) to regain determinism.
These are not acceptable long‑term substitutes for a correct npm ci.
Request
- Re‑triage and reassign this long‑standing Priority 1 issue.
- Provide guidance on the intended
cisemantics vs. semver ranges when a lockfile is present. - If feasible, add an integration test that:
- generates a lock at
t0, - publishes a new minor that satisfies the range,
- verifies
npm ciatt1installs exactly the locked versions without error.
- generates a lock at
I’m happy to help validate patches on affected projects and CI runners.
Old issue for reference: https://github.com/npm/cli/issues/6787
Possible origin of the error
A deeper investigation was made here: https://github.com/npm/cli/issues/6787#issuecomment-1751005219
Expected Behavior
npm installshould produce a lockfile that is immediately considered “in‑sync” bynpm ci.npm cimust install exactly the versions inpackage-lock.json, regardless of newer registry versions that satisfy semver ranges inpackage.json.
Actual Behavior
- After the first
npm install,npm cierrors with an “out of sync” message. - A second
npm installmutates the lockfile, after whichnpm cisucceeds.
Steps to Reproduce (minimal)
-
Create
package.json:{ "name": "testcase", "version": "1.0.0", "devDependencies": { "addons-linter": "6.13.0", "htmlhint": "1.1.4" } } -
Run:
npm install npm ciYou’ll see an error similar to:
npm ERR! `npm ci` can only install packages when your package.json and package-lock.json are in sync. npm ERR! Invalid: lock file's <pkg>@<x.y.z> does not satisfy <pkg>@<a.b.c> -
Run
npm installagain. -
Run
npm ciagain → now it succeeds.
A ready‑made repro (from the original report) also exposes the issue:
https://github.com/npm/cli/files/12568428/testcase.zip
Environments where this has been observed
- Multiple npm majors: 9.x, 10.x, 11.x.
- Node versions vary; the behavior tracks npm CLI, not Node runtime.
- Cross‑version scenarios (e.g., lockfile generated on npm 11.x, CI runs npm 10.x) also trigger mismatches. (These observations consolidate multiple user reports across the original thread and related issues. See #6787)
#8548 #3176
I’m seeing this same behavior on the latest npm release.
A single npm install appears to complete successfully, but the resulting package-lock.json is not actually in sync with package.json. Running npm ci immediately afterward fails with “package.json and package-lock.json are out of sync” error and shows several missing/invalid entries.
If I run npm install a second time—without touching any project files—npm reports additional package additions/removals/changes. After that second install, npm ci succeeds.
This creates a real problem in CI because developers often run npm install once, commit the updated lockfile, and the CI pipeline (which uses npm ci) fails due to the inconsistent lockfile produced by the first install.
The key symptom is:
npm install(first run): completes, but produces a lockfile thatnpm cirejectsnpm install(second run): makes further unexpected changesnpm ci: then works
I can reproduce this every time with the following sequence:
# start from a clean state
$ rm -rf node_modules package-lock.json
$ npm install
npm WARN deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/config-array instead
npm WARN deprecated [email protected]: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated [email protected]: Use your platform's native atob() and btoa() methods instead
npm WARN deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/object-schema instead
npm WARN deprecated [email protected]: Use your platform's native DOMException instead
npm WARN deprecated [email protected]: Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties
npm WARN deprecated [email protected]: This version is no longer supported. Please see https://eslint.org/version-support for other options.
added 1674 packages in 33s
297 packages are looking for funding
run `npm fund` for details
$ npm ci
npm ERR! code EUSAGE
npm ERR!
npm ERR! `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 ERR!
npm ERR! Invalid: lock file's [email protected] does not satisfy [email protected]
npm ERR! Missing: [email protected] from lock file
npm ERR! Missing: [email protected] from lock file
npm ERR! Invalid: lock file's [email protected] does not satisfy [email protected]
npm ERR! Missing: [email protected] from lock file
npm ERR! Missing: [email protected] from lock file
npm ERR! Missing: [email protected] from lock file
npm ERR! Missing: [email protected] from lock file
$ npm install
added 7 packages, removed 20 packages, and changed 2 packages in 1s
297 packages are looking for funding
run `npm fund` for details
$ npm ci
npm WARN deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/config-array instead
npm WARN deprecated [email protected]: Rimraf versions prior to v4 are no longer supported
npm WARN deprecated [email protected]: Use your platform's native atob() and btoa() methods instead
npm WARN deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm WARN deprecated @humanwhocodes/[email protected]: Use @eslint/object-schema instead
npm WARN deprecated [email protected]: Use your platform's native DOMException instead
npm WARN deprecated [email protected]: Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties
npm WARN deprecated [email protected]: This version is no longer supported. Please see https://eslint.org/version-support for other options.
added 1661 packages in ~2m
297 packages are looking for funding
run `npm fund` for details
#8781 #8725
#8724
not sure if it helps, but I have reproduced that on 11.6.2 specifically compared to 11.6.0, a combination of
npm "ci" "--no-save" "--lockfile-version" "3"
and a lock-file that is actually version 2 fails with a seemingly weird error complaining about a dependency missing in the lock file (the specific package I found listed as either nested dep or a optional dep).
With the same version-2 lock file, the following would still pass on 11.6.2
npm "install" "--no-save" "--lockfile-version" "3"
^^ we use these two command lines in build scripts (in CI / local machines), that's why the weird combination of arguments.
For this specific case, the fix was to actually convert the lockfile to version 3 -- after that the first listed command line works fine.
I did that with npm "install" "--lockfile-version" "3".
Maybe it helps shed some light on this issue / on #8669
edit: However for other projects, it seems the lockfiles are already at version 3, however 11.6.2 expects all optional dependencies to be present in the lockfile
I am having the same issue. Anyone has any clue for me? https://github.com/unav4ila8le/foliofox/actions/runs/19952836659/job/57216226094
After we‘ve switched to using npm ci, we‘re seeing the same, see here: https://github.com/js-cookie/js-cookie/actions/runs/19975894159/job/57291864207?pr=903
It is [email protected] in our case.
This comment help me solve the issue with my project: https://github.com/npm/cli/issues/4934#issuecomment-1413032794 ty @lasley
Now I need to solve a lot of @types/node conflicts, yay 😅
While investigating this issue in our projects, I created a validation tool that checks for inconsistencies between package.json and package-lock.json before running npm ci. It's been helpful for catching these sync problems earlier in our development workflow, especially in CI environments. The tool validates:
Version mismatches between package.json semver ranges and locked versions Missing dependencies that should be present in the lockfile Structural inconsistencies that cause npm ci to fail
This is not a fix for the underlying npm bug - it's a diagnostic utility to help identify when lockfiles are out of sync so teams can address issues before CI breaks. I'm sharing it in case it helps others debug their specific cases while waiting for a proper resolution from the npm team.
Install package: npm install npm-ci-guard
Github repository:
https://github.com/yaronpen/npm-ci-guard
inconsistencies between package.json and package-lock.json
so it's not a bc break, but a userland bug?
would be nice if npm at least confirms it yes/no