pnpm/pnpm · error · PnpmError
SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER
SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER
Error message
${specifier} isn't supported by any available resolver. What it means
The default resolver dispatches a wanted dependency through a fixed chain — custom resolvers, npm, jsr:, git, tarball URLs, the local schemes (file:/link:/workspace:), node:/deno:/bun: runtime specifiers, named registries, and finally bare local paths. If every resolver declines, resolve throws SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER naming the alias and bare specifier.
Source
Thrown at pnpm11/resolving/default-resolver/src/index.ts:158
await _resolveFromLocalScheme(wantedDependency as { bareSpecifier: string }, opts)
)) ??
await _resolveNodeRuntime(wantedDependency, opts) ??
await _resolveDenoRuntime(wantedDependency, opts) ??
await _resolveBunRuntime(wantedDependency, opts) ??
// Named-registry runs between the explicit local schemes above and the
// path-shape match below, so `<alias>:@scope/pkg` reaches the configured
// registry while a colliding `file:`/`link:`/`workspace:` alias cannot
// hijack the built-in protocols.
await resolveFromNamedRegistry(wantedDependency, opts as ResolveFromNpmOptions) ??
(wantedDependency.bareSpecifier
? await _resolveFromLocalPath(wantedDependency as { bareSpecifier: string }, opts)
: null)
if (!resolution) {
let specifier = `${wantedDependency.alias ? wantedDependency.alias + '@' : ''}${wantedDependency.bareSpecifier ?? ''}`
if (specifier !== '') {
specifier = `"${specifier}"`
}
throw new PnpmError(
'SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER',
`${specifier} isn't supported by any available resolver.`)
}
return resolution
},
resolveLatest: async (query, opts) => {
const info = (await resolveLatestFromNpm(query, opts)) ??
(await resolveLatestFromJsr(query, opts)) ??
(await resolveLatestFromGit(query)) ??
(await resolveLatestFromTarball(query)) ??
(await resolveLatestFromLocal(query)) ??
(await _resolveLatestNodeRuntime(query, opts)) ??
(await _resolveLatestDenoRuntime(query, opts)) ??
(await _resolveLatestBunRuntime(query, opts)) ??
(await resolveLatestFromNamedRegistry(query, opts))
return info
},
clearCache,View on GitHub (pinned to 6261b7f388)
Solutions
- Fix the specifier spelling — most commonly workspace:*, file:./…, or a valid registry name/URL
- Use a supported form: npm name/range/tag, jsr:@scope/name, git URL (optionally #semver:^1), tarball URL, file:, link:, workspace:, node:/deno:/bun:, or an alias to a named registry
- For proprietary protocols, register a customResolver plugin so the chain has a handler
Example fix
// package.json — before "@scope/tools": "worskpace:*" // after "@scope/tools": "workspace:*"
Defensive patterns
Strategy: type-guard
Validate before calling
import fs from 'node:fs'
// Reject specs no built-in resolver can claim before running install.
export function checkSpecifierSupported (alias: string | undefined, spec: string): void {
const protocols = ['workspace:', 'file:', 'link:', 'jsr:', 'http://', 'https://', 'node:', 'deno:', 'bun:']
const bare = spec
const looksLikeProtocol = /^[a-zA-Z][\w+.-]*:/.test(bare)
if (looksLikeProtocol && !protocols.some((p) => bare.startsWith(p))) {
throw new Error(`Unsupported protocol in ${alias ?? ''}${alias ? '@' : ''}${spec}`)
}
if (!looksLikeProtocol && bare.startsWith('.') && !fs.existsSync(bare)) {
throw new Error(`Local path dependency does not exist: ${bare}`)
}
} Type guard
const SUPPORTED_PREFIXES = ['workspace:', 'file:', 'link:', 'jsr:', 'node:', 'deno:', 'bun:']
const GIT_HOSTS = /^(git|git\+ssh|git\+https|ssh|https?):\/\//
const TARBALL_URL = /^https?:\/\/.+\.(tgz|tar\.gz)(#.*)?$/
export function isResolvableSpecifier (spec: string): boolean {
if (SUPPORTED_PREFIXES.some((p) => spec.startsWith(p))) return true
if (GIT_HOSTS.test(spec) || TARBALL_URL.test(spec)) return true
if (/^\.\.(\/|$)/.test(spec)) return true // bare local path
return /^[^:/]/.test(spec) // plain npm name/range/tag
} Try / catch
try {
await resolveDependencies(deps, opts)
} catch (err) {
if (err instanceof PnpmError && err.code === 'SPEC_NOT_SUPPORTED_BY_ANY_RESOLVER') {
// The message quotes the offending alias/spec; fix the manifest or register a custom resolver
throw new Error(`Unresolvable dependency spec: ${err.message}`)
}
throw err
} Prevention
- Lint package.json specs in CI with isResolvableSpecifier to catch protocol typos like 'worskpace:'
- Add internal deps with pnpm add --workspace so the protocol is always spelled correctly
- For proprietary protocols, register a customResolver plugin before adopting the spec in manifests
When it happens
Trigger: A dependency spec no resolver claims: a typo'd protocol ('worskpace:*', 'htp://…'), a protocol pnpm does not support, an alias whose bare part matches nothing, or a bare path that does not exist on disk so the final path resolver declines.
Common situations: Copy-paste typos in package.json specs; protocols from other ecosystems; a proprietary protocol that requires a customResolver plugin that is not registered; deleted local directories referenced by path.
Related errors
- MISSING_JSR_PACKAGE_SCOPE
- LOGOUT_FAILED
- INVALID_PACKAGE_NAME
- Invalid argument - ${arg}. Rebuild can only select by versio
- MISSING_HOISTED_LOCATIONS
AI-assisted analysis of pnpm/pnpm@6261b7f388 (2026-08-17).
Data as JSON: /api/errors/8050bae199d37584.
Report an issue: GitHub.