{"record":{"id":"9c56b21b03a679e4","repo":"jackwener/OpenCLI","slug":"hn-api-http-res-status-for-item-id","errorCode":null,"errorMessage":"HN API HTTP ${res.status} for item ${id}","messagePattern":"HN API HTTP (.+?) for item (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/hackernews/read.js","lineNumber":21,"sourceCode":" *\n * Mirrors `reddit read` semantics — fetches a story plus a tree of top-level\n * comments and inline replies via the public Firebase API:\n *   https://hacker-news.firebaseio.com/v0/item/<id>.json\n *\n * Output rows:\n *   - first row is the story itself (`type=POST`)\n *   - each subsequent row is a comment, indented by depth (`L0`, `L1`, …)\n *   - `[+N more replies]` summary rows whenever depth/limit cuts in\n */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nconst HN_ITEM_BASE = 'https://hacker-news.firebaseio.com/v0/item';\n\nasync function fetchItem(id) {\n    const res = await fetch(`${HN_ITEM_BASE}/${id}.json`);\n    if (!res.ok) {\n        throw new CommandExecutionError(`HN API HTTP ${res.status} for item ${id}`, 'Check the item ID');\n    }\n    return res.json();\n}\n\nfunction requirePositiveInt(value, label) {\n    if (!Number.isInteger(value) || value <= 0) {\n        throw new ArgumentError(`${label} must be a positive integer`);\n    }\n    return value;\n}\n\nfunction requireMinInt(value, min, label) {\n    if (!Number.isInteger(value) || value < min) {\n        throw new ArgumentError(`${label} must be an integer >= ${min}`);\n    }\n    return value;\n}\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hackernews/read.js#L3-L39","documentation":"fetchItem calls the Hacker News Firebase API (hacker-news.firebaseio.com/v0/item/{id}.json) and throws CommandExecutionError with a hint when the response is not OK. It is used by story, fetched, and replies to load HN items by numeric id.","triggerScenarios":"fetchItem(id) receiving a non-2xx response: malformed/nonexistent numeric id (400/404), HN API outage or rate-limit (403/429/5xx), or Firebase downtime.","commonSituations":"Typing a wrong or too-large story id; HN/Firebase API returning 5xx during incidents; aggressive polling hitting Firebase limits; the v0 endpoint being temporarily unavailable.","solutions":["Verify the item id exists: open https://news.ycombinator.com/item?id=<id> in a browser","Check HN API status (https://hn.algolia.com/api or Firebase health) and retry later","Only pass positive integers — validate with requirePositiveInt before fetching","Add retry with exponential backoff for 429/5xx responses"],"exampleFix":"// before\nawait fetchItem('abc');\n// HN API HTTP 400 for item abc\n\n// after\nconst id = requirePositiveInt(Number(rawId), 'item id');\nconst item = await fetchItem(id);","handlingStrategy":"try-catch","validationCode":"function isValidHnId(v){ return Number.isInteger(v) && v > 0; }\nif (!isValidHnId(id)) throw new Error('HN item id must be a positive integer');","typeGuard":"const isHnHttpError = (e) => e instanceof CommandExecutionError && /HN API HTTP \\d+/.test(e.message);","tryCatchPattern":"try {\n  const item = await fetchItem(id);\n} catch (e) {\n  if (isHnHttpError(e) && /HTTP (429|5\\d\\d)/.test(e.message)) {\n    return retryWithBackoff(() => fetchItem(id), 3);\n  }\n  throw e;\n}","preventionTips":["Validate ids with requirePositiveInt before calling fetchItem","Cache fetched items to reduce API calls and rate-limit risk","Retry only on 429/5xx; 404 means the id genuinely does not exist","Check HN API status during outages before debugging your own code"],"tags":["http","api","hackernews","network"],"backgroundTag":"http-non-2xx-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}