pnpm/pnpm · error · PnpmError
UNUSED_PATCH
UNUSED_PATCH
Error message
The following patches were not used: ${unusedPatches.join(', ')} What it means
After install, every configured patch key must have been applied to something in the resolved graph; leftovers throw UNUSED_PATCH. A patch goes unused when its key matches no installed package/version — the dependency was removed, the locked version drifted outside the key, or the name is typo'd. With `allowUnusedPatches: true` (pnpm settings in the root package.json) it downgrades to a warning.
Source
Thrown at pnpm11/patching/config/src/verifyPatches.ts:29
}
export function verifyPatches ({
patchedDependencies,
appliedPatches,
allowUnusedPatches,
}: VerifyPatchesOptions): void {
const unusedPatches: string[] = []
for (const patchKey of allPatchKeys(patchedDependencies)) {
if (!appliedPatches.has(patchKey)) unusedPatches.push(patchKey)
}
if (!unusedPatches.length) return
const message = `The following patches were not used: ${unusedPatches.join(', ')}`
if (allowUnusedPatches) {
globalWarn(message)
return
}
throw new PnpmError('UNUSED_PATCH', message, {
hint: 'Either remove them from "patchedDependencies" or update them to match packages in your dependencies.',
})
}
View on GitHub (pinned to 6261b7f388)
Solutions
- Remove the stale entry: `pnpm patch-remove <key>`
- Update the key to the version now installed (regenerate with `pnpm patch` if the diff no longer applies)
- Opt out of the hard failure with `"pnpm": { "allowUnusedPatches": true }` in the root package.json — install then only warns
Example fix
// before
"pnpm": { "patchedDependencies": { "foo@1.2.3": "patches/foo.patch" } } // foo@1.3.0 installs
// after
"pnpm": { "patchedDependencies": { "foo@1.3.0": "patches/foo.patch" } } Defensive patterns
Strategy: validation
Validate before calling
import { satisfies, valid } from 'semver'
import { readCurrentLockfile } from '@pnpm/lockfile.fs'
const lockfile = await readCurrentLockfile(path.join(modulesDir, '.pnpm'), { ignoreIncompatible: true })
const snapshots = Object.keys(lockfile?.packages ?? {})
for (const key of Object.keys(patchedDependencies)) {
const at = key.lastIndexOf('@')
const name = at > 0 ? key.slice(0, at) : key
const version = at > 0 ? key.slice(at + 1) : undefined
const matches = snapshots.some(depPath =>
depPath.includes(`/${name}/`) && (version == null || valid(version) != null
? depPath.endsWith(`@${version}`)
: snapshots.some(() => true) && version === '*')
)
if (!matches) console.warn(`Patch key ${key} matches nothing in the lockfile`)
} Try / catch
try {
await installCmd.handler(opts)
} catch (err) {
if (isPnpmErrorCode(err, 'UNUSED_PATCH')) {
// message lists unused keys: remove them (pnpm patch-remove) or set pnpm.allowUnusedPatches=true in the root manifest
} else throw err
} Prevention
- Remove patch entries in the same change that removes or upgrades the patched dependency
- Set `pnpm.allowUnusedPatches: true` in the root package.json when stale entries are acceptable
- After upgrades, compare patchedDependencies keys against installed versions before running install in CI
When it happens
Trigger: Removing or renaming the patched dependency while leaving the patchedDependencies entry; the installed version moves outside the key's exact version or range; a typo'd package name; a patch declared in a workspace root whose packages no longer use the dependency.
Common situations: Installs failing right after a dependency upgrade; dependency cleanup without patch cleanup; generated manifests retaining stale entries.
Related errors
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/6509876a5027c886.
Report an issue: GitHub.