Back to core workflows Fix dependency resolution Tune package metadata Validate before publishing

Fixing npm ETARGET 'No Matching Version Found'

ETARGET means npm contacted the registry, downloaded the package's metadata, and found no published version that satisfies the range you asked for. The package exists; the version does not — or at least not on the registry, tag or network path npm is looking at. The error blocks every install until it is resolved, and in CI it often appears minutes after a release, which makes it look random. This guide walks through the real causes, from typos and unpublished versions to stale caches, scoped registries and dist-tags, and gives a repeatable way to find which one you are hitting.

Exact symptoms and error messages

npm prints the requested range and the package name:

npm error code ETARGET
npm error notarget No matching version found for @acme/design-tokens@^4.2.0.
npm error notarget In most cases you or one of your dependencies are requesting
npm error notarget a package version that doesn't exist.
npm error A complete log of this run can be found in: /home/ci/.npm/_logs/2026-03-02T09_14_11_512Z-debug-0.log

pnpm and Yarn report the same condition with different wording:

 ERR_PNPM_NO_MATCHING_VERSION  No matching version found for @acme/design-tokens@^4.2.0

This error happened while installing a direct dependency of /repo/apps/web

The latest release of @acme/design-tokens is "4.1.3".
➤ YN0082: │ @acme/design-tokens@npm:^4.2.0: No candidates found

pnpm's message is the most useful because it prints the latest version it can see. If that number is lower than the range you requested, the registry npm is talking to genuinely does not have the version yet.

Root cause analysis

npm resolves a range in three steps: fetch the packument (the package's metadata document) from the configured registry, filter its versions by the requested range, and pick the highest match — or the version a dist-tag points at, when the spec is a tag. ETARGET means the filter returned nothing. The general resolution process is described in Dependency Resolution Explained.

Where ETARGET is raised during install npm reads the requested range, fetches the packument from the configured registry or cache, filters versions by the range, and raises ETARGET if none match. requested range @acme/design-token s@^4.2.0 fetch packument registry for this scope, or local cache filter versions semver match, respecting dist-tags no match: ETARGET metadata is fine; the version is not there
ETARGET is raised after metadata is fetched successfully — the package exists, but no version in that metadata satisfies the range.

The filter can come up empty for several distinct reasons:

  1. The version was never published — a typo in the range, a release job that failed after tagging git, or a changeset that bumped the manifest but never published.
  2. The version was just published and npm is reading stale metadata. npm caches packuments and honours HTTP caching headers. Registries and CDNs can take a short while to serve new metadata everywhere, and a CI runner that restored ~/.npm from a cache may hold an older copy.
  3. The request is going to the wrong registry. A scoped package published to GitHub Packages or a private registry, looked up on the public registry (or vice versa), returns metadata for a different package — or a public squatted name with different versions.
  4. The version exists only as a prerelease. A range such as ^4.2.0 never matches 4.2.0-beta.3; semver excludes prereleases unless the range itself names a prerelease on the same major.minor.patch.
  5. The version was unpublished. Unpublished versions disappear from the packument and can never be republished under the same number.

Resolution and configuration patch

Work from the registry outwards: find out what versions the registry you are actually using can see.

# Which registry is used for this scope?
npm config get @acme:registry
npm config get registry

# What versions does that registry report?
npm view @acme/design-tokens versions --json
npm view @acme/design-tokens dist-tags

# Bypass the local cache entirely
npm view @acme/design-tokens@^4.2.0 version --prefer-online
Narrowing down an ETARGET A chain of checks from whether the version appears in npm view, to cache freshness, registry routing, prerelease semantics and unpublishing. Does npm view list the version? Stale cache retry with --prefer-online; clear the restored cache yes Is it on another registry? Fix scope routing @acme:registry in .npmrc must point at it yes no Is it a prerelease only? Use an explicit range ^4.2.0-beta.3 or a dist-tag such as @next yes no Not published fix the range or publish the version no
Run the checks in order; each one rules out a whole class of causes.
  1. Version missing everywhere: correct the range to a published version, or publish the missing version. Check your release pipeline — a tag with no corresponding publish is the usual culprit, covered in Fixing semantic-release Not Publishing a Release.
  2. Stale metadata: re-run with fresh metadata. In CI, avoid restoring the npm cache immediately after publishing a dependency in the same pipeline, or add --prefer-online to the install:
npm install --prefer-online
# or, for a single stubborn package
npm cache clean --force && npm install
  1. Wrong registry: route the scope explicitly in the project .npmrc, so every developer and runner resolves it the same way:
@acme:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
registry=https://registry.npmjs.org/

Multi-registry routing is covered in Routing Scopes to Multiple Registries in .npmrc.

  1. Prerelease only: request it explicitly with npm install @acme/design-tokens@next (a dist-tag) or a range that includes the prerelease, and remember to switch back to a stable range when the release lands.
Which ranges match which versions Shows whether caret, tilde, exact and tag specifiers match a stable version, a newer minor, and a prerelease of the same version. 4.2.0 4.3.1 4.2.0-beta.3 ^4.2.0 match match no match ~4.2.0 match no match no match ^4.2.0-beta.1 match match match 4.2.0 (exact) match no match no match @next (dist-tag) if tagged if tagged if tagged
Prereleases only match ranges that explicitly mention a prerelease on the same version — the usual surprise behind ETARGET on beta packages.

When ETARGET comes from a transitive dependency

Sometimes the failing range is not in your manifest at all. The debug log names the parent chain; look for lines like npm error notarget ... requested by some-plugin@2.0.1. That means a dependency requests a version that does not exist — typically because its maintainer published the plugin before publishing the library it depends on, or because the library version was unpublished after a bad release.

You have three options, in order of preference. First, upgrade or downgrade the parent to a version whose range resolves. Second, use overrides to point the transitive dependency at a version that exists:

{
  "overrides": {
    "some-plugin": {
      "@acme/design-tokens": "4.1.3"
    }
  }
}

Third, and only temporarily, pin the parent in your manifest to its last working version. The override approach is covered in more depth in Pinning Vulnerable Transitive Versions with overrides; the same mechanism works for missing versions as it does for vulnerable ones.

Timing: publishing and consuming in the same pipeline

Monorepos that publish a package and immediately install it elsewhere — a separate deploy repository, a documentation site, an integration-test fixture — hit a timing variant of this error. The publish returns success, the next job starts within seconds, and npm install still sees the old packument from its cache or a CDN edge. Do not paper over it with sleep. Poll for the version instead, with a timeout:

for i in $(seq 1 30); do
  npm view "@acme/design-tokens@4.2.0" version --prefer-online && break
  sleep 10
done

Better still, avoid the round trip: inside a monorepo, consume the package through the workspace protocol so no registry lookup is needed at all.

Proxy registries and mirrors

Organisations that route installs through a proxy — Verdaccio, Artifactory, Nexus or AWS CodeArtifact — add another layer that can hold stale or partial metadata. A proxy fetches a packument from its upstream the first time it is requested, stores it, and serves the stored copy until its cache window expires. A version published upstream a few minutes ago will not appear until that window passes or the proxy revalidates, and every developer and CI runner behind the proxy sees the same stale view, so the failure looks systematic rather than flaky.

Check whether the proxy is the problem by querying the upstream registry directly and comparing:

npm view @acme/design-tokens versions --json --registry=https://registry.npmjs.org/
npm view @acme/design-tokens versions --json   # through the configured proxy

If the upstream list contains the version and the proxy's does not, the fix belongs in the proxy: shorten the metadata cache time for fast-moving scopes (Verdaccio's maxage on the uplink, Artifactory's metadata retrieval cache period), or trigger a manual refresh for the package. Some proxies also block versions by policy — a quarantine period for newly published releases, or a vulnerability rule that hides flagged versions. Those setups return exactly the metadata that produces ETARGET, so ask the platform team whether a policy is filtering the version before you spend time on caches. A quarantine rule is a sensible supply-chain control; the right response is to wait out the window or request an exception, not to bypass the proxy.

CLI validation and debug commands

# Show the full resolution attempt, including registry URLs
npm install @acme/design-tokens@^4.2.0 --loglevel=http

# Inspect the cached packument, if any
npm cache ls 2>/dev/null | grep design-tokens || ls ~/.npm/_cacache

# Confirm which .npmrc files are in effect and in what order
npm config ls -l | grep -E "registry|userconfig|globalconfig"

# After the fix, a clean install must succeed
rm -rf node_modules && npm ci

Prevention and CI/CD guardrails

  • Publish before you consume. Release jobs should publish dependencies before bumping ranges that require them, and verify with npm view before the next stage starts.
  • Route every private scope in a committed .npmrc, so resolution does not depend on a developer's global configuration.
  • Keep prerelease ranges out of main. Lint package.json files for -beta, -rc and -next ranges on your release branch.
  • Never unpublish a version other packages depend on. Deprecate it instead, as described in Deprecating npm Package Versions.

Frequently Asked Questions

Why does ETARGET appear in CI but not on my machine? Your machine likely has fresher metadata or a different .npmrc. CI runners often restore an older npm cache or lack the scope-to-registry mapping from your global user config. Compare npm config ls -l on both.

Is ETARGET ever caused by authentication? Indirectly. Some registries return an empty or public packument instead of a 401 when a request is unauthenticated, so the versions list lacks your private releases. If npm view shows fewer versions than the registry UI does, check the token for that scope.

Can I install a version that was unpublished? No. An unpublished version is removed from the registry permanently and its number cannot be reused. Move to a different published version.

Related

Dependency Resolution Explained