pnpm/pnpm · error · PnpmError
INVALID_PATCH_DIR
INVALID_PATCH_DIR
Error message
${userDir} is not a valid patch directory What it means
`pnpm patch-commit <dir>` only accepts an edit directory created by `pnpm patch`: it reads `<modulesDir>/.pnpm_patches/state.json`, keyed by the absolute edit-dir path, to recover which package the dir belongs to (name, applyToAll). If there is no state entry for the resolved directory, the dir is rejected as not a valid patch directory.
Source
Thrown at pnpm11/patching/commands/src/patchCommit.ts:69
usages: ['pnpm patch-commit <patchDir>'],
})
}
type PatchCommitCommandOptions = install.InstallCommandOptions & Pick<Config, 'patchesDir' | 'patchedDependencies'> & Pick<ConfigContext, 'rootProjectManifest' | 'rootProjectManifestDir'>
export async function handler (opts: PatchCommitCommandOptions, params: string[]): Promise<string | undefined> {
const userDir = params[0]
const lockfileDir = (opts.lockfileDir ?? opts.dir ?? process.cwd()) as ProjectRootDir
const patchesDirName = normalizePath(path.normalize(opts.patchesDir ?? 'patches'))
const patchesDir = path.join(lockfileDir, patchesDirName)
const patchedPkgManifest = await readPackageJsonFromDir(userDir)
const editDir = path.resolve(opts.dir, userDir)
const stateValue = readEditDirState({
editDir,
modulesDir: path.join(lockfileDir, opts.modulesDir ?? 'node_modules'),
})
if (!stateValue) {
throw new PnpmError('INVALID_PATCH_DIR', `${userDir} is not a valid patch directory`, {
hint: 'A valid patch directory should be created by `pnpm patch`',
})
}
const { applyToAll } = stateValue
const nameAndVersion = `${patchedPkgManifest.name}@${patchedPkgManifest.version}`
const patchKey = applyToAll ? patchedPkgManifest.name : nameAndVersion
let gitTarballUrl: string | undefined
if (!applyToAll) {
gitTarballUrl = await getGitTarballUrlFromLockfile({
alias: patchedPkgManifest.name,
bareSpecifier: patchedPkgManifest.version || undefined,
}, {
lockfileDir,
modulesDir: opts.modulesDir,
virtualStoreDir: opts.virtualStoreDir,
})
}
const patchedPkg = parseWantedDependency(gitTarballUrl ? `${patchedPkgManifest.name}@${gitTarballUrl}` : nameAndVersion)View on GitHub (pinned to 6261b7f388)
Solutions
- Re-create the dir properly: `pnpm patch <pkg>`, redo your edits there, then `pnpm patch-commit <printed-path>`
- Pass the exact path pnpm printed and run patch-commit from the same working directory
- Never delete node_modules or move the edit dir between patch and patch-commit
Example fix
# before pnpm patch-commit ./my-manual-edit-dir # ERR_PNPM_INVALID_PATCH_DIR # after pnpm patch lodash # ...edit files in node_modules/.pnpm_patches/lodash@4.17.21... pnpm patch-commit node_modules/.pnpm_patches/lodash@4.17.21
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs'
import path from 'node:path'
const editDir = path.resolve(opts.dir, userDir)
const statePath = path.join(lockfileDir, opts.modulesDir ?? 'node_modules', '.pnpm_patches', 'state.json')
const state = fs.existsSync(statePath) ? JSON.parse(fs.readFileSync(statePath, 'utf8')) : {}
if (!(editDir in state)) {
throw new Error(`${userDir} was not created by pnpm patch; run it first`)
} Type guard
function isPnpmEditDir (dir: string, stateFile: string): boolean {
if (!fs.existsSync(stateFile)) return false
const state = JSON.parse(fs.readFileSync(stateFile, 'utf8')) as Record<string, unknown>
return dir in state
} Prevention
- Always create the edit dir via `pnpm patch` and pass the exact printed path back to patch-commit
- Run patch and patch-commit from the same working directory
- Do not delete node_modules between patch and patch-commit — the state file lives in node_modules/.pnpm_patches/state.json
When it happens
Trigger: Passing an arbitrary directory you edited manually; passing a relative path from a different cwd so path.resolve(opts.dir, userDir) produces a different absolute key than stored; moving or renaming the edit dir after `pnpm patch`; deleting node_modules between patch and patch-commit (state.json lives inside it).
Common situations: Hand-created edit dirs; `rm -rf node_modules` or a fresh CI stage between the patch and commit steps; running patch-commit from a different workspace directory than patch.
Related errors
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/cb3493e52c7f5c8c.
Report an issue: GitHub.