badges/shields · error · InvalidResponse

unparseable jsonl response

Error message

unparseable jsonl response

What it means

parseJsonl parses newline-delimited JSON responses by splitting the buffer on line breaks and JSON.parsing each line. If any line fails to parse, Shields throws InvalidResponse with prettyMessage 'unparseable jsonl response'. This indicates the upstream provider's JSONL feed was malformed or not actually JSONL.

Source

Thrown at core/base-service/jsonl.js:26

 * by newlines, trims each line, filters empty lines, and parses each line
 * as JSON. Throws an `InvalidResponse` error when any line is unparseable.
 *
 * @param {string|Buffer} buffer - The raw response body.
 * @returns {Array<object>} Array of parsed JSON values, one per line.
 */
function parseJsonl(buffer) {
  const logTrace = (...args) => trace.logTrace('fetch', ...args)
  let jsonl
  try {
    jsonl = buffer
      .toString()
      .split(/\r?\n/)
      .map(line => line.trim())
      .filter(Boolean)
      .map(line => JSON.parse(line))
  } catch (err) {
    logTrace(emojic.dart, 'Response JSONL (unparseable)', buffer)
    throw new InvalidResponse({
      prettyMessage: 'unparseable jsonl response',
      underlyingError: err,
    })
  }
  logTrace(emojic.dart, 'Response JSONL (before validation)', jsonl, {
    deep: true,
  })
  return jsonl
}

export { parseJsonl }

View on GitHub (pinned to 766fd8bc89)

Solutions

  1. Inspect the raw buffer logged by Shields trace ('Response JSONL (unparseable)').
  2. Confirm the endpoint returns one JSON value per line (e.g. with curl).
  3. Check for upstream API format changes or migration announcements.
  4. If the provider switched to a single JSON document, use the JSON-based service/base instead.
  5. Handle auth/limits so error pages don't reach the parser.

Example fix

// before (multi-line pretty JSON treated as JSONL)
{"a":
 1}
// after — compact each object onto one line upstream, or parse differently:
const json = JSON.parse(buffer) // use JSON parsing, not JSONL
Defensive patterns

Strategy: validation

Validate before calling

const isProbablyJsonl = text => text.trim().split(/\r?\n/).filter(Boolean).every(line => { try { JSON.parse(line); return true } catch { return false } })

Try / catch

try {
  const jsonl = parseJsonl(buffer)
} catch (err) {
  console.error('bad JSONL line:', String(buffer).split('\n').find(l => { try { JSON.parse(l); return false } catch { return true } }))
  return fallbackBadge
}

Prevention

When it happens

Trigger: Any line of the split, trimmed, non-empty buffer fails JSON.parse inside parseJsonl — e.g. provider returns an HTML error page, a plain-text message, or a JSON object spanning multiple lines where JSONL was expected.

Common situations: Upstream JSONL format change (pretty-printed multi-line JSON instead of one object per line), rate-limit HTML page, empty or partial body from a truncated response, wrong endpoint used for a JSONL feed.

Related errors


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