badges/shields · error · InvalidResponse
unparseable json response
Error message
unparseable json response
What it means
parseJson parses an upstream response buffer with JSON.parse. When the body is not valid JSON, Shields throws InvalidResponse with prettyMessage 'unparseable json response'. This distinguishes 'provider responded but sent garbage/HTML' from 'provider unreachable', typically an error page, login HTML, or truncated body.
Source
Thrown at core/base-service/json.js:20
import emojic from 'emojic'
import { InvalidResponse } from './errors.js'
import trace from './trace.js'
/**
* Parse a JSON response buffer. Throws an `InvalidResponse` error when the
* JSON is unparseable.
*
* @param {string|Buffer} buffer - The raw response body.
* @returns {object|Array} The parsed JSON value.
*/
function parseJson(buffer) {
const logTrace = (...args) => trace.logTrace('fetch', ...args)
let json
try {
json = JSON.parse(buffer)
} catch (err) {
logTrace(emojic.dart, 'Response JSON (unparseable)', buffer)
throw new InvalidResponse({
prettyMessage: 'unparseable json response',
underlyingError: err,
})
}
logTrace(emojic.dart, 'Response JSON (before validation)', json, {
deep: true,
})
return json
}
export { parseJson }
View on GitHub (pinned to 766fd8bc89)
Solutions
- Log/inspect the raw response body (Shields traces it via logTrace as 'Response JSON (unparseable)').
- Verify API credentials — an expired token often yields HTML error pages.
- Check the upstream URL and any API version in the path.
- Confirm the endpoint actually returns application/json (curl with -i).
- Add caching/rate limiting if the provider is throttling you into error pages.
Example fix
// before
const res = await fetch(url) // returns HTML login page
const json = await res.json() // InvalidResponse: unparseable json response
// after
const text = await res.text()
if (!text.trim().startsWith('{') && !text.trim().startsWith('[')) throw new Error('non-JSON response: ' + text.slice(0, 100))
const json = JSON.parse(text) Defensive patterns
Strategy: validation
Validate before calling
const looksLikeJson = text => { const t = text.trimStart(); return t.startsWith('{') || t.startsWith('[') }
if (!looksLikeJson(buffer)) throw new Error('upstream returned non-JSON body') Type guard
function isJsonObject(v) { return typeof v === 'object' && v !== null && !Array.isArray(v) } Try / catch
try {
const json = JSON.parse(buffer)
} catch (err) {
console.error('non-JSON body (first 200 chars):', String(buffer).slice(0, 200))
return fallbackBadge
} Prevention
- Always check response Content-Type before parsing.
- Log the raw body on parse failure to spot HTML error/login pages.
- Keep API tokens valid — expired auth often yields HTML.
- Watch upstream provider changelogs for content-type changes.
When it happens
Trigger: JSON.parse(buffer) throws inside parseJson — upstream returned HTML (login/error page), empty body, gzip/charset mangling, or a JSONL/truncated response where a JSON schema was expected.
Common situations: Provider rate-limits with an HTML error page, auth token expired causing a redirect to a login page, CDN/proxy injecting interstitials, upstream API version change altering content-type.
Related errors
- unparseable intermediate json response
- unparseable jsonl response
- response.error || 'service error'
- service error
- unparseable svg response
AI-assisted analysis of badges/shields@766fd8bc89 (2026-08-30).
Data as JSON: /api/errors/b2b9998828b7e593.
Report an issue: GitHub.