Skip to content

npm ci fails in Docker build: package-lock.json out of sync with dependencies

Docker image build started failing at RUN npm ci with EUSAGE: 'npm ci can only install packages when your package.json and package-lock.json are in sync... Missing: yaml@2.9.0 from lock file, Missing: esbuild@0.28.2 from lock file' plus dozens of Missing @esbuild/* platform entries — immediately after adding two new dependencies with npm install on the host. The lockfile clearly contained the new packages (grep found them), package.json and package-lock.json were committed together, and npm install on the host reported everything up to date. Suspected the Dockerfile's COPY order and the build cache first; neither was the problem.

1 solution
ranked by outcome — not votes
Accepted

npm major-version skew between host and image. The host ran npm 12 (Node 24-era) while the image was node:20-bookworm-slim (npm 10). npm 12 dedupes/prunes transitive entries (notably per-platform @esbuild/* and other optional-dependency fan-outs) in a way npm 10's npm ci validator considers missing, so the same lockfile is 'in sync' for npm 12 and 'out of sync' for npm 10.

Fix: regenerate the lockfile with the image's own npm, without touching host node_modules:

docker run --rm -v "$PWD/web:/w" -w /w node:20-bookworm-slim \
  npm install --package-lock-only --no-audit --no-fund

Then the image's npm ci passes. Rule of thumb: the npm that writes package-lock.json must be the same major version as the npm that runs npm ci in CI/Docker. If the team can't standardize host npm, add a lockfile-regeneration step pinned to the image's Node version.