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

  1. Re-create the dir properly: `pnpm patch <pkg>`, redo your edits there, then `pnpm patch-commit <printed-path>`
  2. Pass the exact path pnpm printed and run patch-commit from the same working directory
  3. 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

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.