pnpm/pnpm · error · NoMatchingVersionError

NO_MATCHING_VERSION

NO_MATCHING_VERSION

Error message

No matching version found for ${dep} while fetching it from ${opts.registry}

What it means

NoMatchingVersionError is thrown after a packument was successfully fetched but no version satisfies the requested range or dist-tag — and the workspace fallback either found no better answer or rethrew its own workspace-specific error. The error class (exported from @pnpm/resolving.npm-resolver) carries the full `packageMeta`, so handlers can inspect `meta.versions` and `meta['dist-tags']` programmatically instead of parsing the message.

Source

Thrown at pnpm11/resolving/npm-resolver/src/index.ts:711

          projectDir: opts.projectDir,
          lockfileDir: opts.lockfileDir,
          hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
          update: false,
          saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
          calcSpecifier: opts.calcSpecifier,
          rangeSpecStyle: opts.rangeSpecStyle,
        })
      } catch (workspaceErr) {
        // Neither the registry nor the workspace has a matching version; the
        // workspace mismatch error carries the available local versions,
        // which is the actionable detail here.
        if ((workspaceErr as { code?: string }).code === 'ERR_PNPM_NO_MATCHING_VERSION_INSIDE_WORKSPACE') {
          throw workspaceErr
        }
      }
    }

    throw new NoMatchingVersionError({ wantedDependency, packageMeta: meta, registry })
  } else if (opts.trustPolicy === 'no-downgrade') {
    failIfTrustDowngraded(meta, pickedPackage.version, opts)
  }

  const latest = latestAllowedByPolicy(meta, opts)
  const workspacePkgsMatchingName = workspacePackages?.get(pickedPackage.name)
  if (workspacePkgsMatchingName && opts.projectDir) {
    const matchedPkg = workspacePkgsMatchingName.get(pickedPackage.version)
    if (matchedPkg) {
      return {
        ...resolveFromLocalPackage(matchedPkg, spec, {
          wantedDependency,
          projectDir: opts.projectDir,
          lockfileDir: opts.lockfileDir,
          hardLinkLocalPackages: opts.injectWorkspacePackages === true || wantedDependency.injected,
          saveWorkspaceProtocol: ctx.saveWorkspaceProtocol,
          calcSpecifier: opts.calcSpecifier,
          rangeSpecStyle: opts.rangeSpecStyle,

View on GitHub (pinned to 6261b7f388)

Solutions

  1. See what actually exists: pnpm view <pkg> versions --json and pnpm view <pkg> dist-tags
  2. Fix the range or tag to an existing version (foo@^1.9.0, or the real dist-tag)
  3. If minimumReleaseAge filtered everything out, exclude the package with minimumReleaseAgeExclude: ["pkg@<exact-version>"] or shorten the window
  4. If the package is meant to come from the workspace, declare it with the workspace: protocol so resolution prefers local packages

Example fix

// before (package.json) — only 1.x is published
"dep": "foo@^2.0.0"

// after
"dep": "foo@^1.9.0"
Defensive patterns

Strategy: try-catch

Validate before calling

import semver from 'semver'
// before installing, confirm the range/tag exists in the packument
export async function rangeExists (registry: string, name: string, range: string): Promise<boolean> {
  const meta = await (await fetch(`${registry}/${encodeURIComponent(name).replace('%40', '@')}`)).json()
  const versions = Object.keys(meta.versions ?? {})
  if (semver.validRange(range)) return versions.some(v => semver.satisfies(v, range))
  return Boolean(meta['dist-tags']?.[range])
}

Type guard

import { NoMatchingVersionError } from '@pnpm/resolving.npm-resolver'
function isNoMatchingVersionError (err: unknown): err is NoMatchingVersionError {
  return err instanceof NoMatchingVersionError ||
    (typeof err === 'object' && err !== null && (err as { code?: string }).code === 'ERR_PNPM_NO_MATCHING_VERSION')
}

Try / catch

try {
  const result = await npmResolver.resolve(spec, opts)
} catch (err) {
  if (isNoMatchingVersionError(err)) {
    const versions = Object.keys(err.packageMeta.versions ?? {}).sort(semver.rcompare)
    const tags = err.packageMeta['dist-tags'] ?? {}
    // report or fall back: suggest the closest satisfying version or the real dist-tag
    throw new Error(`No match for ${spec.pref}; published: ${versions.slice(0, 5).join(', ')}; tags: ${JSON.stringify(tags)}`)
  }
  throw err
}

Prevention

When it happens

Trigger: Resolution of `foo@^2.0.0` when the registry packument for `foo` only contains 1.x; a dist-tag that does not exist (`foo@next` when only `latest` is defined); every candidate version filtered out by minimumReleaseAge/publishedBy policies; then the workspace lookup (line ~700-712) also fails and falls through to `throw new NoMatchingVersionError(...)`.

Common situations: Bumping a range before the version is published; typos in dist-tags (`next` vs `nightly`); supply-chain maturity policies (minimumReleaseAge) silently filtering the only matching version; stale local metadata caches in CI.

Related errors


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