badges/shields · warning · InvalidResponse

invalid null license

Error message

invalid null license

What it means

The crates.io license badge service throws InvalidResponse with prettyMessage 'invalid null license' in CratesLicense.transform when the resolved version object from the crates.io API response has no `license` field (null/undefined/empty). This means the upstream API returned data the service cannot render a license badge from.

Source

Thrown at services/crates/crates-license.service.js:42

          {
            name: 'crate',
            example: 'rustc-serialize',
          },
          {
            name: 'version',
            example: '0.3.24',
          },
        ),
      },
    },
  }

  static defaultBadgeData = { label: 'license', color: 'blue' }

  static transform(response) {
    const license = this.getVersionObj(response).license
    if (!license) {
      throw new InvalidResponse({ prettyMessage: 'invalid null license' })
    }

    return { license }
  }

  async handle({ crate, version }) {
    const json = await this.fetch({ crate, version })
    const { license } = this.constructor.transform(json)
    return { message: license }
  }
}

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Verify the crate name and version in the badge URL are correct and the version exists on crates.io
  2. Check crates.io directly (https://crates.io/api/v1/crates/<crate>/<version>) to confirm `license` is present for that version
  3. Omit the version parameter to use the latest version, which is more likely to have a license
  4. If you maintain the crate, add a `license` field to Cargo.toml and publish a new version

Example fix

// before
/badge/crates/license/my-old-crate/0.1.0  ->  invalid null license
// after
/badge/crates/license/my-old-crate  (use latest version that declares a license)
Defensive patterns

Strategy: validation

Validate before calling

async function hasLicense(crate, version) {
  const url = `https://crates.io/api/v1/crates/${crate}${version ? '/' + version : ''}`
  const res = await fetch(url, { headers: { 'User-Agent': 'badge-check' } })
  if (!res.ok) return false
  const data = await res.json()
  const v = version ? data.versions?.find(x => x.num === version) : data.versions?.[0]
  return Boolean(v?.license)
}

Type guard

function hasLicense(v) {
  return typeof v?.license === 'string' && v.license.length > 0
}

Try / catch

try {
  const badge = await getCratesLicenseBadge(crate, version)
} catch (err) {
  if (err.prettyMessage === 'invalid null license') {
    badge = 'license unknown'
  } else throw err
}

Prevention

When it happens

Trigger: Requesting a license badge for a crate whose version entry (default or specified via ?version=) has no `license` key in its crates.io API response - e.g. versions published before license metadata was required, or a version that doesn't exist so getVersionObj falls back to an object without license.

Common situations: Very old crates published before crates.io mandated a license field; querying a specific old version; typos in the crate name or version leading to a partial/empty version object.

Related errors


AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30). Data as JSON: /api/errors/e39e1c241cbb05e1. Report an issue: GitHub.