npm lockfileVersion Mismatch in CI - Fix package-lock Version Conflicts
package-lock.json carries a lockfileVersion. When CI runs a different npm major than the one that wrote the lockfile, the tree can churn, warn, or fail to install reproducibly.
What this error means
CI rewrites package-lock.json on every run, prints warnings about an old lockfile, or npm ci complains the lockfile is out of sync because the runner npm reads or writes a different lockfileVersion than your local npm.
npm WARN read-shrinkwrap This version of npm is compatible with
npm WARN read-shrinkwrap lockfileVersion@1, but package-lock.json was
npm WARN read-shrinkwrap generated for lockfileVersion@3. I'll try to do my best.Diagnose it: reproduce the CI install locally
Install failures are usually environment drift rather than a broken lockfile: a different package-manager major, a different Node version, or a cache that is being restored from a run with different inputs. Reproduce the CI conditions before changing the lockfile, because regenerating it hides the real cause.
# match the runner exactly, then install from a clean slate
node --version && npm --version
rm -rf node_modules
npm ci --foreground-scripts
# if that succeeds locally but fails in CI, the difference is the cache
# or the package-manager version, not your lockfileCommon causes
CI npm major differs from the local npm major
npm 6 writes lockfileVersion 1, npm 7+ writes 2, and npm 9+ writes 3; mixing versions across machines makes the lockfile drift.
A regenerated lockfile committed from a different npm
A teammate or tool on another npm version rewrites the lockfile, and CI then disagrees with it.
How to fix it
Pin one npm version everywhere
- Choose a single npm major for local and CI.
- Install it in CI before running npm ci.
npm install -g npm@10
npm ciRegenerate the lockfile with the pinned version
- Delete package-lock.json and reinstall with the chosen npm.
- Commit the regenerated lockfile.
rm package-lock.json
npm installVerify the fix survives a cold cache
A green run immediately after a fix often proves nothing, because it restored a cache written before the change. Force a cold install once to confirm the fix is real.
# temporarily bust the cache key to prove the fix on a cold runner
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
# then bump this suffix once, run, and remove it
# key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-v2How to prevent it
- Pin the npm version via setup-node or packageManager, regenerate the lockfile only with that version, and run npm ci in CI so the lockfile stays authoritative.