jackwener/OpenCLI · error · CommandExecutionError
Stack Exchange API HTTP ${res.status} for ${label}
Error message
Stack Exchange API HTTP ${res.status} for ${label} What it means
fetchJson throws this CommandExecutionError when the Stack Exchange API responds with an HTTP status other than 200 and other than 404 (i.e. !res.ok). The message embeds the numeric status and the resource label; the hint points at wrong ids and the API's 300 requests/day per-IP quota. It surfaces server-side rejection the caller cannot fix by retrying blindly.
Source
Thrown at clis/stackoverflow/read.js:44
const SE_SITE = 'stackoverflow';
const SE_MAX_PAGE_SIZE = 100;
async function fetchJson(url, label) {
let res;
try {
res = await fetch(url);
} catch (e) {
const detail = e instanceof Error ? e.message : String(e);
throw new CommandExecutionError(
`Network failure fetching ${label}: ${detail}`,
'Check connectivity to api.stackexchange.com',
);
}
if (res.status === 404) {
throw new EmptyResultError(label, `${label} not found`);
}
if (!res.ok) {
throw new CommandExecutionError(
`Stack Exchange API HTTP ${res.status} for ${label}`,
'Check the question id and quota (300/day per IP)',
);
}
let json;
try {
json = await res.json();
} catch (e) {
const detail = e instanceof Error ? e.message : String(e);
throw new CommandExecutionError(
`Malformed JSON from Stack Exchange API for ${label}: ${detail}`,
'The API returned a non-JSON body — likely a transient outage',
);
}
if (json && json.error_id) {
throw new CommandExecutionError(
`Stack Exchange API error ${json.error_id} (${json.error_name}) for ${label}: ${json.error_message || ''}`,
'Common causes: invalid filter, throttled, or quota exhausted',View on GitHub (pinned to 49907e53dc)
Solutions
- Check the question id is a valid number; fix typos causing HTTP 400
- Wait for quota reset if near the 300/day per-IP limit, or register a Stack Exchange app/key to raise the quota
- Add backoff and avoid parallel bursts of requests
- For 5xx, retry later — likely a Stack Exchange-side outage (check status.stackexchange.com)
Example fix
// before
for (const id of ids) results.push(await getQuestion(id));
// after
for (const id of ids) {
try {
results.push(await getQuestion(id));
} catch (e) {
if (String(e.message).includes('HTTP 429')) await sleep(60000);
results.push(await getQuestion(id));
}
} Defensive patterns
Strategy: try-catch
Validate before calling
function assertNumericId(id) {
if (!/^\d+$/.test(String(id).trim())) {
throw new TypeError(`Invalid id (would cause HTTP 400): ${id}`);
}
} Try / catch
async function getWithRateLimitGuard(fn, ...args) {
try {
return await fn(...args);
} catch (e) {
const m = /HTTP (\d+)/.exec(e?.message ?? '');
if (m && (m[1] === '429' || m[1] === '502' || m[1] === '503')) {
await new Promise(r => setTimeout(r, 30000));
return fn(...args);
}
throw e;
}
} Prevention
- Respect the 300 requests/day anonymous per-IP quota; register an API key for higher limits
- Serialize requests and add delays instead of firing them in parallel
- Watch for HTTP 400 caused by non-numeric or malformed ids
- Check status.stackexchange.com before large runs; defer batches during incidents
When it happens
Trigger: Any caller (qData, answersData, acceptedData, qCommentsData, ansCommentsData) receives e.g. HTTP 400 (malformed id/parameter), 429 or the API's quota exhaustion responses, 502/503 during Stack Exchange outages, or 5xx from an intermediary.
Common situations: Batch scripts looping over many questions exhausting the 300/day anonymous per-IP quota (especially shared office/VPN IPs); sending non-numeric ids causing 400; Stack Exchange maintenance windows returning 5xx; rate limiting from aggressive parallel requests.
Related errors
- API_ERROR
- API_ERROR
- Chess.com API returned HTTP ${resp.status} for ${url}
- Flomo API returned HTTP ${resp.status}
- ${label} failed: HTTP ${response.status}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/2909dd62cc14cf5f.
Report an issue: GitHub.