[BUG] npm ci rejects a lockfile just generated by npm install for an unavailable optional dependency
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 10.1k
- Forks
- 4.7k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 19
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/
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked mdream-npm-ci-reproduction repository and run the listed npm 12.0.2 install and ci --dry-run commands, then compare the generated lockfile with npm 11.13.0. Read the optional-dependency handling from fix #9083 and trace why the unavailable package is missing during ci validation. Done means an unmodified lockfile from install is accepted by ci consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100