{"record":{"id":"2909dd62cc14cf5f","repo":"jackwener/OpenCLI","slug":"stack-exchange-api-http-res-status-for-label","errorCode":null,"errorMessage":"Stack Exchange API HTTP ${res.status} for ${label}","messagePattern":"Stack Exchange API HTTP (.+?) for (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/stackoverflow/read.js","lineNumber":44,"sourceCode":"const SE_SITE = 'stackoverflow';\nconst SE_MAX_PAGE_SIZE = 100;\n\nasync function fetchJson(url, label) {\n    let res;\n    try {\n        res = await fetch(url);\n    } catch (e) {\n        const detail = e instanceof Error ? e.message : String(e);\n        throw new CommandExecutionError(\n            `Network failure fetching ${label}: ${detail}`,\n            'Check connectivity to api.stackexchange.com',\n        );\n    }\n    if (res.status === 404) {\n        throw new EmptyResultError(label, `${label} not found`);\n    }\n    if (!res.ok) {\n        throw new CommandExecutionError(\n            `Stack Exchange API HTTP ${res.status} for ${label}`,\n            'Check the question id and quota (300/day per IP)',\n        );\n    }\n    let json;\n    try {\n        json = await res.json();\n    } catch (e) {\n        const detail = e instanceof Error ? e.message : String(e);\n        throw new CommandExecutionError(\n            `Malformed JSON from Stack Exchange API for ${label}: ${detail}`,\n            'The API returned a non-JSON body — likely a transient outage',\n        );\n    }\n    if (json && json.error_id) {\n        throw new CommandExecutionError(\n            `Stack Exchange API error ${json.error_id} (${json.error_name}) for ${label}: ${json.error_message || ''}`,\n            'Common causes: invalid filter, throttled, or quota exhausted',","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/stackoverflow/read.js#L26-L62","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nfor (const id of ids) results.push(await getQuestion(id));\n\n// after\nfor (const id of ids) {\n  try {\n    results.push(await getQuestion(id));\n  } catch (e) {\n    if (String(e.message).includes('HTTP 429')) await sleep(60000);\n    results.push(await getQuestion(id));\n  }\n}","handlingStrategy":"try-catch","validationCode":"function assertNumericId(id) {\n  if (!/^\\d+$/.test(String(id).trim())) {\n    throw new TypeError(`Invalid id (would cause HTTP 400): ${id}`);\n  }\n}","typeGuard":null,"tryCatchPattern":"async function getWithRateLimitGuard(fn, ...args) {\n  try {\n    return await fn(...args);\n  } catch (e) {\n    const m = /HTTP (\\d+)/.exec(e?.message ?? '');\n    if (m && (m[1] === '429' || m[1] === '502' || m[1] === '503')) {\n      await new Promise(r => setTimeout(r, 30000));\n      return fn(...args);\n    }\n    throw e;\n  }\n}","preventionTips":["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"],"tags":["http-error","rate-limit","stackexchange-api","quota"],"backgroundTag":"api-rate-limit-exceeded","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}