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
- See what actually exists: pnpm view <pkg> versions --json and pnpm view <pkg> dist-tags
- Fix the range or tag to an existing version (foo@^1.9.0, or the real dist-tag)
- If minimumReleaseAge filtered everything out, exclude the package with minimumReleaseAgeExclude: ["pkg@<exact-version>"] or shorten the window
- 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
- Check `pnpm view <pkg> versions` and `dist-tags` before pinning or bumping ranges
- When enabling minimumReleaseAge/publishedBy policies, pre-plan excludes for packages that publish fresh versions you need
- In automation, catch NoMatchingVersionError and use err.packageMeta to pick a fallback instead of parsing the message
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
- NO_MATCHING_VERSION_INSIDE_WORKSPACE
- UNPUBLISHED_PKG
- INVALID_${key.replace(/([A-Z])/g, '_$1').toUpperCase()}
- INVALID_VERSION_UNION
- INVALID_ALLOWED_VERSION_SELECTOR
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/d94a457a5f0f0c2e.
Report an issue: GitHub.