Repository navigation
[BUG] npm ci rejects a lockfile just generated by npm install for an unavailable optional dependency #9846
Description
Is there an existing issue for this?
- I have searched the existing issues
Related issues include #8767 and #8726, but both are closed. In #8767, a recurrence after the previous fix was reported and a contributor requested a new issue with a minimal reproduction. This reproduction also appears related to the fix in #9083.
This issue exists in the latest npm version
- I am using the latest npm
Reproduced with npm 12.0.2.
This is not just a request to bump a dependency for a CVE
- This is not solely a request to bump a dependency for a CVE
Current Behavior
When a package declares an optional dependency that is unavailable from the registry, npm 12.0.2 completes a normal npm install and generates package-lock.json. An immediate npm ci using the same npm version rejects that unmodified lockfile as out of sync.
The minimal reproduction uses mdream@1.5.12, which declares the unpublished @mdream/rust-wasm32-wasi@1.5.12 package in optionalDependencies.
npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json and package-lock.json are in sync.
npm error Missing: @mdream/rust-wasm32-wasi@ from lock file
The result differs by npm version:
| npm version | npm install |
Subsequent npm ci |
WASI placeholder in lockfile |
|---|---|---|---|
| 11.13.0 | succeeds | succeeds | generated |
| 11.16.0 | succeeds | fails with EUSAGE |
not generated |
| 12.0.2 | succeeds | fails with EUSAGE |
not generated |
npm 11.13.0 adds this inert placeholder entry and accepts the lockfile:
"node_modules/mdream/node_modules/@mdream/rust-wasm32-wasi": {
"optional": true
}npm 11.16.0 and 12.0.2 do not add the placeholder and then report that dependency as missing during npm ci.
Adding --omit=optional to npm ci does not avoid the error because lockfile validation happens first.
Expected Behavior
npm ci should accept an unmodified package-lock.json generated moments earlier by npm install with the same npm version and configuration.
An unavailable optional dependency should either be represented consistently in the generated lockfile or ignored consistently during npm ci validation.
This appears to be a regression of the optional-dependency handling addressed by #9083 (fix(ci): don't error on optional deps in the lockfile).
Steps To Reproduce
Minimal reproduction repository:
https://github.com/kotering/mdream-npm-ci-reproduction
-
Clone the repository into a fresh directory.
-
Run a normal install using the latest npm:
npx --yes npm@12.0.2 install --ignore-scripts --no-audit --no-fund
-
Without modifying
package.jsonor the generatedpackage-lock.json, run:npx --yes npm@12.0.2 ci --dry-run --ignore-scripts --no-audit --no-fund
-
Observe
EUSAGEandMissing: @mdream/rust-wasm32-wasi@ from lock file. -
The same sequence fails with npm 11.16.0. In a separate fresh checkout, the same sequence succeeds with npm 11.13.0.
Environment
- npm: 12.0.2; also reproduced with 11.16.0
- Node.js: 24.18.0
- OS Name: macOS 26.5.2
- System Model Name: Apple Silicon (arm64)
- npm registry:
https://registry.npmjs.org/
Activity
Merge pull request #1446 from Open-MBEE/docs/sonar-s8543-node-install
docs(ci): record why client/node installs with npm install and that S…