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

  1. Check existing tags with `npm dist-tags ls <package>` and use a valid one
  2. Drop the ?tag parameter to fall back to 'latest'
  3. Create the tag if you own the package: `npm dist-tag add <pkg>@<version> <tag>`
  4. 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

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.