badges/shields · error · InvalidResponse

unparseable svg response

Error message

unparseable svg response

What it means

valueFromSvgBadge strips leading whitespace from the SVG string and runs a `valueMatcher` regex to extract a value from the badge; if the regex does not match, it throws InvalidResponse 'unparseable svg response' with an underlyingError containing the full SVG. The upstream service responded with SVG that does not match the expected badge structure.

Source

Thrown at core/base-service/base-svg-scraping.js:38

class BaseSvgScrapingService extends BaseService {
  /**
   * Extract a value from SVG
   *
   * @param {string} svg SVG to parse
   * @param {RegExp} [valueMatcher=defaultValueMatcher]
   *    RegExp to match the value we want to parse from the SVG
   * @returns {string} Matched value
   */
  static valueFromSvgBadge(svg, valueMatcher = defaultValueMatcher) {
    if (typeof svg !== 'string') {
      throw new TypeError('Parameter should be a string')
    }
    const stripped = svg.replace(leadingWhitespace, '')
    const match = valueMatcher.exec(stripped)
    if (match) {
      return match[1]
    } else {
      throw new InvalidResponse({
        prettyMessage: 'unparseable svg response',
        underlyingError: Error(`Can't get value from SVG:\n${svg}`),
      })
    }
  }

  static headers = { Accept: 'image/svg+xml' }

  /**
   * Request data from an endpoint serving SVG,
   * parse a value from it and validate against a schema
   *
   * @param {object} attrs Refer to individual attrs
   * @param {Joi} attrs.schema Joi schema to validate the response against
   * @param {RegExp} attrs.valueMatcher
   *    RegExp to match the value we want to parse from the SVG
   * @param {string} attrs.url URL to request
   * @param {object} [attrs.options={}] Options to pass to got. See

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Inspect the underlyingError message (it embeds the SVG) to see what was actually returned
  2. Confirm the badge exists and the URL/parameters are correct — error badges often have different markup
  3. Update the service's valueMatcher regex to match the new SVG structure
  4. Add handling/fallback for non-data badges (e.g. treat 'not found' badges as an upstream error instead of parsing)

Example fix

// before
const value = await service._requestSvg({ url }) // regex expects <text id="...">4.2</text>
// after (service subclass)
const valueMatcher = /<text[^>]*>([^<]+)<\/text>[\s\S]*?<text[^>]*>([^<]+)<\/text>/ // updated for new badge markup
const value = await service._requestSvg({ url, valueMatcher })
Defensive patterns

Strategy: try-catch

Validate before calling

function isSvgContainingMatch(body, valueMatcher) {
  return typeof body === 'string' && body.trim().startsWith('<') && body.includes('<svg') && valueMatcher.test(body.replace(/^\s*/, ''))
}
if (!isSvgContainingMatch(buffer, expectedMatcher)) throw new Error('unexpected svg badge body')

Try / catch

try {
  const value = await service._requestSvg({ url })
} catch (err) {
  if (err.prettyMessage === 'unparseable svg response') {
    console.error('SVG body was:', err.underlyingError?.message) // embedded SVG shows what actually came back
  } else throw err
}

Prevention

When it happens

Trigger: The fetched SVG badge lacks the text/element the matcher expects — e.g. the remote service redesigned its badge, returned a 'badge not found' SVG, returned an error page saved as .svg, or the regex in the service subclass no longer fits the markup.

Common situations: Upstream badge format changes after site redesigns; rate-limit or maintenance pages served as SVG; private/repo-not-found responses; shields.io badge style parameter changes altering markup.

Related errors


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