badges/shields · error · NotFound
tag not found
Error message
tag not found
What it means
The npm last-update service fetches full package metadata, reads packageData['dist-tags'][tag] for the requested (or default) tag, and throws NotFound 'tag not found' when the lookup is falsy. This is a plain undefined check, so it fires both when the tag is absent and when the dist-tags object itself is missing. The badge then shows the service's not-found state.
Source
Thrown at services/npm/npm-last-update.service.js:70
}
static defaultBadgeData = { label: 'last updated' }
async handle(namedParams, queryParams) {
const { scope, packageName, tag, registryUrl } =
this.constructor.unpackParams(namedParams, queryParams)
const packageData = await this.fetch({
registryUrl,
scope,
packageName,
schema: fullSchema,
})
const tagVersion = packageData['dist-tags'][tag]
if (!tagVersion) {
throw new NotFound({ prettyMessage: 'tag not found' })
}
return renderDateBadge(packageData.time[tagVersion])
}
}
export class NpmLastUpdate extends NpmBase {
static category = 'activity'
static route = this.buildRoute('npm/last-update', { withTag: false })
static openApi = {
'/npm/last-update/{packageName}': {
get: {
summary: 'NPM Last Update',
parameters: [
pathParam({
name: 'packageName',View on GitHub (pinned to 766fd8bc89)
Solutions
- Check existing tags with `npm dist-tags ls <package>` and use a valid one
- Drop the ?tag parameter to fall back to 'latest'
- Create the tag if you own the package: `npm dist-tag add <pkg>@<version> <tag>`
- Verify spelling and case of the tag in the badge URL
Example fix
// before /npm-last-update/express/badge.svg?tag=edge // no 'edge' tag // after /npm-last-update/express/badge.svg // defaults to 'latest'
Defensive patterns
Strategy: validation
Validate before calling
const meta = await fetch(`https://registry.npmjs.org/${pkg}`).then(r => r.json())
if (!meta['dist-tags'] || !(tag || 'latest' in meta['dist-tags'])) {
throw new Error(`tag '${tag}' not found; have: ${Object.keys(meta['dist-tags'] || {})}`)
} Type guard
function tagExists(meta, tag) {
const t = tag || 'latest'
return Boolean(meta && meta['dist-tags'] && meta['dist-tags'][t])
} Try / catch
try {
return await lastUpdateBadge({ pkg, tag })
} catch (e) {
if (e instanceof NotFound && e.message === 'tag not found') {
return lastUpdateBadge({ pkg }) // default to latest
}
throw e
} Prevention
- Verify the tag exists with `npm dist-tags ls <pkg>` before embedding it in a URL
- Omit ?tag for the common latest-publish-date case
- Check tag spelling and casing — dist-tag keys are case-sensitive
- If you removed a release, remember its dist-tag may have been deleted too
When it happens
Trigger: Badge URL with ?tag=x where packageData['dist-tags'] has no 'x' key; default 'latest' lookup on malformed metadata lacking dist-tags; tag names with different casing than published.
Common situations: Querying prerelease tags (next/canary) on packages that never created them; tags removed after deprecation; typos such as ?tag=Latst.
Related errors
AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30).
Data as JSON: /api/errors/fa7d4b31b22f9668.
Report an issue: GitHub.