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

  1. Remove the stale entry: `pnpm patch-remove <key>`
  2. Update the key to the version now installed (regenerate with `pnpm patch` if the diff no longer applies)
  3. 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

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.