pnpm/pnpm · error · PnpmError

FILTER_CHANGED

FILTER_CHANGED

Error message

Filtering by changed packages failed. ${'stderr' in err ? err.stderr as string : ''}

What it means

Changed-package filtering (pnpm11/workspace/projects-filter/src/getChangedProjects.ts:80) shells out to `git diff --name-only --end-of-options <commit> -- <workingDir>` to list files changed since a base ref. Any non-zero git exit is wrapped as FILTER_CHANGED with git's stderr appended, so the underlying git failure (usually a bad or missing revision) is visible.

Source

Thrown at pnpm11/workspace/projects-filter/src/getChangedProjects.ts:80

async function getChangedDirsSinceCommit (commit: string, workingDir: string, testPattern: string[], changedFilesIgnorePattern: string[]): Promise<ChangedDir[]> {
  let diff!: string
  try {
    diff = (
      await execa('git', [
        'diff',
        '--name-only',
        // Keeps an option-like `<since>` (`--output=...`) from being
        // parsed as a git option — git rejects it as a bad revision.
        '--end-of-options',
        commit,
        '--',
        workingDir,
      ], { cwd: workingDir })
    ).stdout as string
  } catch (err: unknown) {
    assert(util.types.isNativeError(err))
    throw new PnpmError('FILTER_CHANGED', `Filtering by changed packages failed. ${'stderr' in err ? err.stderr as string : ''}`)
  }
  const changedDirs = new Map<string, ChangeType>()

  if (!diff) {
    return []
  }

  const allChangedFiles = diff.split('\n')
    // The prefix and suffix '"' are appended to the Korean path
    .map(line => line.replace(/^"/, '').replace(/"$/, ''))
  const patterns = changedFilesIgnorePattern.filter(
    (pattern) => pattern.length
  )
  const changedFiles = (patterns.length > 0)
    ? micromatch.default.not(allChangedFiles, patterns, {
      dot: true,
    })
    : allChangedFiles

View on GitHub (pinned to 6261b7f388)

Solutions

  1. Confirm the ref resolves locally: `git cat-file -t <since>`; fix typos / use origin/main.
  2. Deepen or unshallow the clone so the base commit exists: `git fetch --unshallow` or increase fetch-depth.
  3. Run the command inside the repository working tree (not an exported copy).

Example fix

# before: shallow clone missing the base ref
$ pnpm --filter='...@origin/main^' build   # ERR_PNPM_FILTER_CHANGED: bad revision

# after
$ git fetch --unshallow origin main   # or: git fetch --depth=500 origin main
$ pnpm --filter='...@origin/main^' build
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from 'node:child_process'

function refExists (since: string, cwd: string): boolean {
  try {
    execSync(`git cat-file -e ${JSON.stringify(since)}^{commit}`, { cwd, stdio: 'ignore' })
    return true
  } catch {
    return false
  }
}
if (!refExists(since, repoDir)) {
  await fetchDeeply(repoDir) // e.g. git fetch --unshallow
}

Try / catch

try {
  await runFiltered({ since })
} catch (err) {
  if ((err as NodeJS.ErrnoException).code === 'ERR_PNPM_FILTER_CHANGED' && /bad revision|unknown revision/.test(String(err.message))) {
    await gitFetchDeeply() // fix the clone, then retry once
    return runFiltered({ since })
  }
  throw err
}

Prevention

When it happens

Trigger: Using `--filter ...@<since>` (or otherwise triggering changed-files filtering) with a `<since>` that git cannot resolve: typo'd ref, a base commit absent from a shallow clone, running outside a git repository, or a corrupted .git.

Common situations: CI shallow clones (fetch-depth too small) where the base commit was never fetched; typo'd since value like 'mainn'; pipelines running on exported tarballs without .git.

Related errors


AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17). Data as JSON: /api/errors/1b350d288288f7ac. Report an issue: GitHub.