Claude Code or Cursor builds a new feature and casually runs npm install lodash. package-lock.json jumps from 5,000 lines to 5,400. You push, CI fails instantly with npm ci can only install packages when your package.json and package-lock.json are in sync, or a teammate pulls and pnpm install --frozen-lockfile refuses. Open the file: 4,000 lines of <<<<<< / >>>>>>. Hand-merging is hopeless.
Fastest fix (do this first): never hand-resolve a lockfile. Reset it to main, keep only the legitimate package.json edits, then regenerate a clean lockfile with the project’s actual package manager:
git checkout origin/main -- package-lock.json # or pnpm-lock.yaml / yarn.lock / bun.lock
npm install --package-lock-only # regen lockfile-only with YOUR manager (table below)
rm -rf node_modules && npm ci # verify it matches in frozen mode
The rest of this guide explains how to tell which of the six failure buckets you’re in, the exact regen command per manager (npm / pnpm / yarn / bun — three of which changed flags or behavior in the last year), and why hand-resolving a lockfile is always wrong.
Which bucket are you in?
Match your symptom to the cause before touching anything. The first three account for roughly 80% of cases.
| Symptom you see | Bucket | Root cause |
|---|---|---|
Two lockfiles now exist (package-lock.json + pnpm-lock.yaml) | 1 | Agent used the wrong package manager |
A dep is in package.json but absent from the lockfile | 2 | package.json edited, install never ran |
| Lockfile diff touches dozens of packages you never named | 3 | Install ran while branch was behind main |
Module not found in prod for a low-level package | 4 | Agent deleted a transitive dependency |
ERESOLVE / ERR_PNPM_PEER_DEP_ISSUES on install | 5 | Version bump triggered a peer-dep cascade |
| Only one of two monorepo lockfiles changed | 6 | Partial workspace update |
Common causes (in detail)
1. Agent ran npm install foo but the project uses pnpm / yarn / bun
The classic. CLAUDE.md / AGENTS.md / .cursorrules didn’t pin a package manager, the agent defaulted to npm install, but the repo is pnpm. Now you have a fresh package-lock.json and a stale pnpm-lock.yaml. CI rejects it. As of pnpm 10/11 the frozen-install error reads:
ERR_PNPM_OUTDATED_LOCKFILE Cannot proceed with the frozen installation.
The lockfile (pnpm-lock.yaml) is outdated and is not up to date with package.json
How to spot it: two lockfiles exist where there should be one, or the lockfile’s lockfileVersion field at the top changed.
2. AI edited package.json but never re-ran install
Aider or Codex added "axios": "^1.7.0" to dependencies but, due to a sandbox or permissions block, never executed the install. The lockfile is untouched, so npm ci blows up on CI:
npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json
npm error and package-lock.json or npm-shrinkwrap.json are in sync.
How to spot it: a dependency listed in package.json does not exist anywhere in the lockfile. Grep the lockfile for the package name; if there’s no hit, you’re here.
3. AI ran install while local was behind main’s new deps
Your branch is three new dependencies behind main. The agent runs npm install something-else locally; npm doesn’t just install that one package — it re-resolves and re-pins dozens of transitive deps it considers stale. Fifteen hundred lines of unrelated lockfile churn land in the diff and the rebase explodes.
How to spot it: the lockfile diff shows additions/removals of packages you never touched. git diff origin/main -- pnpm-lock.yaml | grep -c '^+' returns a number wildly larger than the one or two deps you actually changed.
4. AI deleted a package thinking it was unused — it was transitive
The agent grepped for import statements, didn’t see explicit references to tslib / regenerator-runtime / core-js, and removed them from dependencies. Dev runs fine; the production build dies because something transitively required it:
Module not found: Can't resolve 'tslib'
How to spot it: the removed package is a low-level runtime (tslib, @babel/runtime, core-js, regenerator-runtime), or npm ls <pkg> shows multiple parents pulling it in.
5. AI upgraded one package and triggered a peer-dep cascade
You asked the agent to bump react from 18 to 19. It dutifully re-pinned the peer-dependent libraries. One of them has no React-19-compatible release yet, so the lockfile contains an unresolvable entry.
How to spot it: install reports ERESOLVE could not resolve (npm) or ERR_PNPM_PEER_DEP_ISSUES (pnpm).
6. Two lockfiles modified in a monorepo
Root lockfile and a workspace lockfile both exist; the agent updated only one. (Modern pnpm and bun use a single root lockfile for the whole workspace, so this mostly bites older npm/yarn-workspaces setups or repos with nested independent installs.)
How to spot it: under pnpm-workspace.yaml or a package.json workspaces field, two lockfile modification times are out of sync.
One more thing that isn’t your fault
If npm ci fails on a lockfile you didn’t change, a transitive dependency may have published a new patch since the lockfile was committed. For example, a fresh @types/node patch can make npm ci reject an otherwise-valid lockfile. The fix is the same — regenerate with npm install --package-lock-only and commit — but the cause is upstream, not the agent.
Shortest path to fix
Core rule: never hand-resolve a lockfile conflict — regenerate it. A lockfile is a derived artifact; merging its conflict markers by hand produces a file that matches neither side’s dependency tree.
Step 1: Reset the lockfile to main entirely
Don’t open the 4,000-line <<<<<< mess. Just take main’s version:
git checkout origin/main -- package-lock.json
# or, for your manager:
git checkout origin/main -- pnpm-lock.yaml
git checkout origin/main -- yarn.lock
git checkout origin/main -- bun.lock
Monorepo, resolve every conflicted lockfile at once:
git checkout origin/main -- $(git diff --name-only --diff-filter=U | grep -E '(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lock)$')
This throws the AI’s polluted lockfile away while preserving any legitimate package.json additions, which are tracked separately.
Step 2: Audit what actually changed in package.json
git diff origin/main -- package.json
For each added dependency, ask:
- Is this actually needed? Agents often add “just in case” packages.
- Is the version range your repo’s style (caret / tilde / pinned), or did the agent write something loose like
*orlatest? - Should it be in
dependenciesordevDependencies? (Type packages — anything@types/*— and build tooling belong in devDeps.)
Manually delete anything that doesn’t survive that audit before you regenerate.
Step 3: Regenerate the lockfile with the project’s package manager
Check which manager the repo expects first:
grep '"packageManager"' package.json
# e.g. "packageManager": "pnpm@11.7.0"
Then run only the matching lockfile-only command. These flags changed recently, so use exactly the one for your manager (verified June 2026):
| Manager | Regenerate command | Notes |
|---|---|---|
| npm | npm install --package-lock-only | Updates package-lock.json only; doesn’t touch node_modules. |
| pnpm | pnpm install --lockfile-only | Writes pnpm-lock.yaml only. Since pnpm 10.9 it re-resolves the whole graph, so expect a larger but correct diff. |
| Yarn Berry (v2+) | yarn install --mode=update-lockfile | Berry-only flag; updates yarn.lock without a full fetch. |
| Yarn Classic (v1) | yarn install (then commit yarn.lock) | Classic has no lockfile-only mode; a normal install regenerates and writes yarn.lock. |
| bun | bun install --lockfile-only | Writes the text bun.lock (the default since Bun 1.2; the old binary bun.lockb is legacy). |
The lockfile-only flags update the lockfile without installing into node_modules — much faster, and they won’t pollute the directory.
Step 4: Verify locally in frozen mode
Reproduce exactly what CI does:
rm -rf node_modules
pnpm install --frozen-lockfile # or: npm ci / yarn install --immutable / bun install --frozen-lockfile
npm test && npm run build
--frozen-lockfile / npm ci / --immutable all refuse to mutate the lockfile and fail loudly on any drift — the same gate CI uses. If this passes locally, CI will pass.
Step 5: If Step 4 reports a peer-dep error
npm error ERESOLVE could not resolve
npm error While resolving: react-dom@19.0.0
npm error Found: react@18.3.1
Go back to package.json and bump or pin both sides of the conflict together, then regenerate:
npm pkg set dependencies.react=19.0.0 dependencies.react-dom=19.0.0
npm install --package-lock-only
If the offending peer genuinely has no compatible release yet, force a resolution with overrides (npm/pnpm) or resolutions (yarn) — but treat this as a temporary patch and remove it once the upstream release lands:
{
"pnpm": {
"overrides": {
"react": "19.0.0"
}
}
}
How to confirm it’s fixed
You’re done when all three are true:
- Exactly one lockfile exists for your project (run
ls *lock* */*lock* 2>/dev/null— no straypackage-lock.jsonnext to apnpm-lock.yaml). - A clean frozen install passes:
rm -rf node_modules && pnpm install --frozen-lockfile(ornpm ci) exits 0. - The
package.jsondiff againstmaincontains only the deps you intended, with no lockfile conflict markers (git grep -n '<<<<<<<'returns nothing).
Prevention
- Pin
packageManagerinpackage.json(e.g."packageManager": "pnpm@11.7.0"). Note that this is enforced only if Corepack is enabled (corepack enable) — and as of Node.js 25 (October 2025) Corepack is no longer bundled with Node and must be installed separately. The field alone is a hint, not a guarantee; pair it with the CI check below. - First line of
CLAUDE.md/.cursorrules/AGENTS.md: “Package manager is pnpm; never runnpm installoryarn add.” - A pre-commit hook that rejects two lockfiles existing simultaneously (
package-lock.json+pnpm-lock.yaml). - When the agent wants a new dep, require it to state “I want X because Y” and approve before execution — keep
Bash(npm install:*)denied by default and allowlist on demand. - CI must run
npm ci/pnpm install --frozen-lockfile/yarn install --immutable; any drift fails the build before it lands. This is the only enforcement that actually holds. - Run dependency upgrades through the dedicated AI dependency upgrade workflow — don’t mix them into feature branches.
FAQ
Why can’t I just hand-merge the lockfile conflict?
A lockfile is generated from your package.json and the registry’s dependency graph. Picking lines from each side of a <<<<<<< block yields a file that describes a tree neither branch actually resolves to — integrity hashes won’t match, and npm ci will reject it. Always regenerate.
npm ci fails but I never changed the lockfile. Why?
Most likely a transitive dependency published a new patch after your lockfile was committed (a fresh @types/* or similar). Run npm install --package-lock-only to re-pin against what’s currently published, then commit. Less commonly, the lockfile was generated by a different npm major version than CI runs — align the packageManager field.
Which package manager does this repo use?
Check grep '"packageManager"' package.json, then which lockfile exists: package-lock.json (npm), pnpm-lock.yaml (pnpm), yarn.lock (yarn), or bun.lock / legacy bun.lockb (bun). If more than one exists, that’s bucket 1 — delete the wrong one.
The agent deleted a package and now the prod build can’t find it. Restore it?
Yes, if npm ls <pkg> shows other packages depend on it, it was a real (often transitive) requirement. Add it back to dependencies and regenerate. Buckets like this are why deleting deps based on import grep alone is unsafe.
Should I commit the lockfile at all? For applications, yes — always commit it so every install (and CI) is reproducible. For published libraries the lockfile is not used by consumers, but committing it still makes your own CI deterministic.
pnpm regenerated way more lines than I expected — is that a bug?
No. Since pnpm 10.9, --lockfile-only re-resolves the full graph rather than touching only the changed package, so the diff can be large. As long as pnpm install --frozen-lockfile then passes, the lockfile is correct.
Related
- AI code broke build
- Merge conflict after AI edits
- AI pre-commit review workflow
- Claude Code SEO audit
- AI dependency upgrade workflow
Tags: #AI coding #Debug #Troubleshooting