pnpm ERR_PNPM_OUTDATED_LOCKFILE in CI - Fix Lockfile Out of Sync
pnpm runs with --frozen-lockfile by default in CI. ERR_PNPM_OUTDATED_LOCKFILE means installing would change pnpm-lock.yaml, so package.json and the lockfile disagree.
What this error means
The CI install step fails with ERR_PNPM_OUTDATED_LOCKFILE, noting that the lockfile is not up to date with package.json. Locally pnpm install succeeds because it can update the lockfile.
ERR_PNPM_OUTDATED_LOCKFILE Cannot perform a frozen installation
because the lockfile is not up to date with package.json
* 1 dependencies were added: left-pad@^1.3.0Diagnose 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
package.json changed without updating the lockfile
A dependency was added or bumped without re-running pnpm install, so pnpm-lock.yaml lags behind.
The lockfile was not committed
A regenerated lockfile stayed local, so CI checks out a stale version.
How to fix it
Regenerate and commit the lockfile
- Run pnpm install locally to update pnpm-lock.yaml.
- Commit the lockfile so CI is in sync.
pnpm install
git add pnpm-lock.yaml
git commit -m "Update pnpm-lock.yaml"Verify 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
- Commit pnpm-lock.yaml with every dependency change, pin pnpm via packageManager, and keep --frozen-lockfile in CI so drift fails fast before merge.