badges/shields · error · NotFound
tag not found
Error message
tag not found
What it means
The npm version service fetches a package's dist-tags document from `${registryUrl}/-/package/${slug}/dist-tags` and throws NotFound 'tag not found' when a tag parameter was supplied but is not a key in that object. This is an explicit `in` check on the dist-tags map, so it precisely detects unknown tags; a 404 from the registry is separately mapped to 'package not found'.
Source
Thrown at services/npm/npm-version.service.js:85
}
async handle(namedParams, queryParams) {
const { scope, packageName, tag, registryUrl } =
this.constructor.unpackParams(namedParams, queryParams)
const slug =
scope === undefined
? packageName
: this.constructor.encodeScopedPackage({ scope, packageName })
const packageData = await this._requestJson({
schema,
url: `${registryUrl}/-/package/${slug}/dist-tags`,
httpErrors: { 404: 'package not found' },
})
if (tag && !(tag in packageData)) {
throw new NotFound({ prettyMessage: 'tag not found' })
}
return this.constructor.render({
tag,
version: packageData[tag || 'latest'],
})
}
}
View on GitHub (pinned to 766fd8bc89)
Solutions
- Fetch the dist-tags endpoint and pick an existing key for the ?tag parameter
- Remove ?tag to use the implicit 'latest' entry
- Publish/point the tag on the target registry: `npm dist-tag add --registry <url>`
- Correct the tag spelling/casing in the badge URL
Example fix
// before /npm/v/lodash/badge.svg?tag=stable // 'stable' not in dist-tags // after /npm/v/lodash/badge.svg?tag=latest
Defensive patterns
Strategy: validation
Validate before calling
const distTags = await fetch(`${registry}/-/package/${encodeURIComponent(pkg).replace('%2F', '/')}/dist-tags`).then(r => r.json())
if (tag && !(tag in distTags)) {
throw new Error(`tag '${tag}' not found; available: ${Object.keys(distTags).join(', ')}`)
} Type guard
function hasTag(distTags, tag) {
return typeof distTags === 'object' && distTags !== null && (tag ? tag in distTags : 'latest' in distTags)
} Try / catch
try {
return await versionBadge({ pkg, tag })
} catch (e) {
if (e instanceof NotFound && e.message === 'tag not found') {
const available = await fetchDistTags(pkg)
throw new Error(`tag '${tag}' not found. Available: ${Object.keys(available).join(', ')}`)
}
throw e
} Prevention
- Fetch and display available dist-tags to users instead of guessing tag names
- Use the same registry for tag discovery and badge rendering — tags differ per registry
- Watch for tag renames upstream and update badge URLs accordingly
- For scoped packages, encode the name correctly in the dist-tags URL slug
When it happens
Trigger: GET badge with ?tag=foo where foo is not in the package's dist-tags response; querying a package on a custom registry where that tag was never published; slug (scoped package encoding) fine but tag misspelled.
Common situations: Using tags from one registry against another registry/mirror; tags renamed (e.g. 'next' -> 'beta') breaking old badge URLs; case-sensitive tag mismatch.
Related errors
AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30).
Data as JSON: /api/errors/72376966f9656198.
Report an issue: GitHub.