jackwener/OpenCLI · error · CommandExecutionError
stack exchange returned malformed JSON: ${error?.message ||
Error message
stack exchange returned malformed JSON: ${error?.message || error} What it means
seFetch parses the Stack Exchange API response with resp.json(); when the body is not valid JSON it wraps the parse failure in a CommandExecutionError with the underlying parser message. The Stack Exchange API normally always returns JSON, so this usually means a proxy, captive portal, or HTML error page intercepted the response.
Source
Thrown at clis/stackoverflow/utils.js:74
'User-Agent': UA,
},
});
} catch (error) {
throw new CommandExecutionError(`stack exchange request failed: ${error?.message || error}`);
}
if (resp.status === 429) {
throw new CommandExecutionError('stack exchange returned HTTP 429 (rate limited)', 'Wait a few seconds and retry, or lower --limit.');
}
if (!resp.ok) {
let body = '';
try { body = (await resp.json())?.error_message || ''; } catch { /* ignore */ }
throw new CommandExecutionError(`stack exchange HTTP ${resp.status}: ${body || resp.statusText}`);
}
let data;
try {
data = await resp.json();
} catch (error) {
throw new CommandExecutionError(`stack exchange returned malformed JSON: ${error?.message || error}`);
}
if (data?.error_id) {
throw new CommandExecutionError(
`stack exchange API error: ${data.error_message || data.error_name}`,
'Inspect the URL in a browser for the canonical error context.',
);
}
return data;
}
/** Convert SE epoch seconds to YYYY-MM-DD. */
export function epochToDate(value) {
if (value == null || value === '') return '';
const n = Number(value);
if (!Number.isFinite(n) || n <= 0) return '';
return new Date(n * 1000).toISOString().slice(0, 10);
}
View on GitHub (pinned to 49907e53dc)
Solutions
- Re-run the command to rule out a transient proxy/captive-portal response
- Check connectivity: curl -sS 'https://api.stackexchange.com/2.3/sites?pagesize=1' and confirm JSON output
- Disable or bypass the corporate proxy/VPN for api.stackexchange.com
- Inspect the raw body (the preceding HTTP-status error would have shown it) to identify what is being returned instead of JSON
Example fix
// before
try {
data = await resp.json();
} catch (error) {
throw new CommandExecutionError(`stack exchange returned malformed JSON: ${error?.message || error}`);
}
// after
const body = await resp.text();
let data;
try {
data = JSON.parse(body);
} catch (error) {
throw new CommandExecutionError(`stack exchange returned malformed JSON: ${error?.message || error}; body started: ${body.slice(0, 120)}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
const pre = await fetch(url, { method: 'HEAD' });
const ct = pre.headers.get('content-type') || '';
if (!ct.includes('application/json')) throw new Error(`expected JSON, got ${ct}`); Type guard
function isJsonContentType(resp) {
return (resp.headers.get('content-type') || '').includes('application/json');
} Try / catch
try {
const data = await seFetch(url);
} catch (e) {
if (/malformed JSON/.test(e.message)) {
// network/proxy returned non-JSON; surface body snippet and retry or abort
} else throw e;
} Prevention
- Verify api.stackexchange.com returns JSON with curl before scripting
- Bypass corporate proxies/VPN or set NO_PROXY for the API host
- Log the raw response body on parse failure to identify interceptors
- Retry once on transient gateway errors
When it happens
Trigger: Network intermediaries return an HTML page (login portal, 502 from a proxy/CDN) with HTTP 200; the API returns an empty body; a wrong or redirecting URL yields HTML instead of JSON.
Common situations: Corporate proxy or hotel/airport wifi captive portal intercepting requests to api.stackexchange.com; rate limiting handled by an edge that serves HTML; TLS interception appliances rewriting the response.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- ${label} returned malformed JSON: ${err?.message ?? err}
- ${label} returned a non-JSON response
- archive snapshots returned malformed JSON: ${error?.message
- `${label} returned malformed JSON: ${err?.message ?? err}`
- ${label} returned malformed JSON: ${err?.message ?? err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/d813e687eaea3151.
Report an issue: GitHub.