badges/shields · error · ValidationError

Unexpected field '${key}'. Allowed values are (${expectedKey

Error message

Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})

What it means

makeBadge only accepts a fixed set of keys: label, message, labelColor, color, style, logoBase64, links, idSuffix. In _clean (badge-maker/lib/index.js:74-84), any key that is not in this whitelist throws — including null-valued unknown keys — because a null/unknown key falls into the else branch. This catches typos and stale options early.

Source

Thrown at badge-maker/lib/index.js:80

  const expectedKeys = [
    'label',
    'message',
    'labelColor',
    'color',
    'style',
    'logoBase64',
    'links',
    'idSuffix',
  ]

  const cleaned = {}
  Object.keys(format).forEach(key => {
    if (format[key] != null && key === 'logoBase64') {
      cleaned.logo = format[key]
    } else if (format[key] != null && expectedKeys.includes(key)) {
      cleaned[key] = format[key]
    } else {
      throw new ValidationError(
        `Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`,
      )
    }
  })

  // Legacy.
  cleaned.label = cleaned.label || ''

  return cleaned
}

/**
 * Create a badge
 *
 * @param {object} format Object specifying badge data
 * @param {string} format.label (Optional) Badge label (e.g: 'build')
 * @param {string} format.message (Required) Badge message (e.g: 'passing')
 * @param {string} format.labelColor (Optional) Label color

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Remove unknown keys or fix the typo so the key matches the whitelist exactly
  2. For optional fields, omit the key rather than setting it to null — note null values for KNOWN keys also throw here
  3. Filter the descriptor: Object.fromEntries(Object.entries(o).filter(([k, v]) => v != null))

Example fix

// before
const svg = makeBadge({ label: 'build', message: 'passing', colour: 'green' })
// after
const svg = makeBadge({ label: 'build', message: 'passing', color: 'green' })
Defensive patterns

Strategy: type-guard

Validate before calling

const ALLOWED = ['label', 'message', 'labelColor', 'color', 'style', 'logoBase64', 'links', 'idSuffix']
for (const key of Object.keys(badge)) {
  if (!ALLOWED.includes(key) || badge[key] == null) {
    throw new Error(`bad descriptor key/value: ${key}`)
  }
}
const svg = makeBadge(badge)

Type guard

function isCleanDescriptor(v) {
  const allowed = ['label','message','labelColor','color','style','logoBase64','links','idSuffix']
  return typeof v === 'object' && v !== null &&
    Object.keys(v).every(k => allowed.includes(k) && v[k] != null)
}

Try / catch

try {
  return makeBadge(badge)
} catch (e) {
  if (e instanceof ValidationError && e.message.startsWith('Unexpected field')) {
    const key = e.message.match(/'(.+)'/)[1]
    const { [key]: _, ...rest } = badge
    return makeBadge(rest)
  }
  throw e
}

Prevention

When it happens

Trigger: makeBadge({ message: 'x', colours: 'green' }) (typo), makeBadge({ message: 'x', labelColor: null, color: null }) (explicit nulls hit the else branch since format[key] != null fails), or passing extra metadata keys (e.g. { message: 'x', id: 1 }).

Common situations: Misspelling an option name ('colour' vs 'color', 'logo' vs 'logoBase64'), spreading an unrelated config object into the descriptor, carrying over option names from other badge libraries, or explicitly setting a field to null expecting it to be ignored.

Related errors


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