Self-Healing CI: Recovering from npm ci Lockfile Drift
npm ci fails on lockfile drift by design: it installs exactly what the lockfile says and refuses to resolve a package.json that no longer agrees with it.
The problem
npm ci fails reporting a package missing from the lock file, or a locked version that does not satisfy the range in package.json. The build works locally, where npm install quietly reconciles the two. The lockfile was not regenerated after a dependency change, usually because a merge resolved package.json without regenerating the lock.
npm ERR! Missing: foo@1.0.0 from lock fileWhy it happens
npm ci deliberately does not resolve. It exists to install a lockfile reproducibly, so any disagreement is an error rather than something to be fixed silently, which is exactly why it is the right command for CI.
Merges are the common origin: two branches each add a dependency, the package.json conflict is resolved by hand, and the lockfile is regenerated by neither.
The manual fix
Manual mitigations for lockfile drift:
- Run npm install locally to regenerate package-lock.json, then commit it.
- Do not resolve lockfile merge conflicts by hand; regenerate the file instead.
- Keep npm ci in CI rather than switching to npm install, so drift stays visible instead of being masked.
npm install && git add package-lock.json && git commit -m "regenerate lockfile"How this gets automated
This one is different from the network cases, and the difference is worth being precise about: a retry alone changes nothing, because the second npm ci fails exactly like the first. Recovery requires running a different command, falling back to npm install so the lockfile can be resolved, which unblocks the current run without pretending the drift is not there. The durable fix is still a committed lockfile, and that belongs in a pull request a person reviews rather than in a silent change to the repository.