AI Edits Broke package-lock / pnpm-lock — Fix the Conflict

An agent ran an install or removed deps and now your lockfile is at war with the team's. Throw it away and regenerate — here's the exact path per package manager.

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 seeBucketRoot cause
Two lockfiles now exist (package-lock.json + pnpm-lock.yaml)1Agent used the wrong package manager
A dep is in package.json but absent from the lockfile2package.json edited, install never ran
Lockfile diff touches dozens of packages you never named3Install ran while branch was behind main
Module not found in prod for a low-level package4Agent deleted a transitive dependency
ERESOLVE / ERR_PNPM_PEER_DEP_ISSUES on install5Version bump triggered a peer-dep cascade
Only one of two monorepo lockfiles changed6Partial 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:

  1. Is this actually needed? Agents often add “just in case” packages.
  2. Is the version range your repo’s style (caret / tilde / pinned), or did the agent write something loose like * or latest?
  3. Should it be in dependencies or devDependencies? (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):

ManagerRegenerate commandNotes
npmnpm install --package-lock-onlyUpdates package-lock.json only; doesn’t touch node_modules.
pnpmpnpm install --lockfile-onlyWrites 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-lockfileBerry-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.
bunbun install --lockfile-onlyWrites 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:

  1. Exactly one lockfile exists for your project (run ls *lock* */*lock* 2>/dev/null — no stray package-lock.json next to a pnpm-lock.yaml).
  2. A clean frozen install passes: rm -rf node_modules && pnpm install --frozen-lockfile (or npm ci) exits 0.
  3. The package.json diff against main contains only the deps you intended, with no lockfile conflict markers (git grep -n '<<<<<<<' returns nothing).

Prevention

  • Pin packageManager in package.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 run npm install or yarn 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.

Tags: #AI coding #Debug #Troubleshooting