pnpm ERR_PNPM_PEER_DEP_ISSUES - Fix Unmet Peer Dependencies in CI
pnpm checks peer dependencies strictly and, with strict-peer-dependencies on, fails the install when peers are unmet - surfacing conflicts npm/yarn might only warn about.
What this error means
pnpm install fails with ERR_PNPM_PEER_DEP_ISSUES, listing packages whose peer dependencies are missing or version-mismatched. The real fix is to satisfy the peers, not to blanket-disable the check.
ERR_PNPM_PEER_DEP_ISSUES Unmet peer dependencies
.
└─┬ @some/ui 3.1.0
└── ✕ unmet peer react@^18.0.0: found 17.0.2Diagnose 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
A peer dependency is unmet or mismatched
A package needs a peer (e.g. React 18) that is absent or at an incompatible version in your tree.
strict-peer-dependencies turns warnings into failures
pnpm fails the install on unmet peers under strict mode, where other managers might only warn.
How to fix it
Satisfy the peer (preferred)
Install or align the required peer versions.
pnpm add react@^18 react-dom@^18
# or pin an override in package.json:
# "pnpm": { "overrides": { "react": "^18" } }Use peerDependencyRules deliberately
- For a known-safe mismatch, allow it via
pnpm.peerDependencyRulesrather than disabling all strictness. - Avoid blanket
strict-peer-dependencies=false; it hides real incompatibilities. - Regenerate the lockfile after resolving peers.
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
- Keep packages and their peers on compatible majors.
- Use peerDependencyRules for specific, vetted exceptions.
- Commit the lockfile and install consistently.