{"record":{"id":"c242af7d958a9acf","repo":"jackwener/OpenCLI","slug":"hackernews-id","errorCode":null,"errorMessage":"hackernews/${id}","messagePattern":"hackernews/(.+?)","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"warning","filePath":"clis/hackernews/read.js","lineNumber":100,"sourceCode":"        { name: 'limit', type: 'int', default: 25, help: 'Max top-level comments' },\n        { name: 'depth', type: 'int', default: 2, help: 'Max reply depth (1=no replies, 2=one level of replies, etc.)' },\n        { name: 'replies', type: 'int', default: 5, help: 'Max replies shown per comment at each level' },\n        { name: 'max-length', type: 'int', default: 2000, help: 'Max characters per comment body (min 100)' },\n    ],\n    columns: ['type', 'author', 'score', 'text'],\n    func: async (args) => {\n        const id = String(args.id || '').trim();\n        if (!/^\\d+$/.test(id)) {\n            throw new ArgumentError(`Invalid HN item id: ${args.id}`, 'Pass a numeric id like 39847301');\n        }\n        const limit = requirePositiveInt(args.limit ?? 25, 'hackernews read --limit');\n        const maxDepth = requirePositiveInt(args.depth ?? 2, 'hackernews read --depth');\n        const maxReplies = requirePositiveInt(args.replies ?? 5, 'hackernews read --replies');\n        const maxLength = requireMinInt(args['max-length'] ?? 2000, 100, 'hackernews read --max-length');\n\n        const story = await fetchItem(id);\n        if (!story || story.deleted || story.dead) {\n            throw new EmptyResultError(`hackernews/${id}`, 'Story not found, deleted, or dead');\n        }\n\n        const results = [];\n\n        // Story header row. text combines title + selftext (Ask/Show HN body) + external URL.\n        const storyBodyRaw = htmlToText(story.text || '');\n        const storyBody = storyBodyRaw.length > maxLength\n            ? storyBodyRaw.slice(0, maxLength) + '\\n... [truncated]'\n            : storyBodyRaw;\n        const storyParts = [story.title || ''];\n        if (storyBody) storyParts.push('\\n' + storyBody);\n        if (story.url) storyParts.push('\\n' + story.url);\n        results.push({\n            type: 'POST',\n            author: story.by || '[deleted]',\n            score: story.score ?? 0,\n            text: storyParts.join('').trim(),\n        });","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hackernews/read.js#L82-L118","documentation":"`EmptyResultError` is thrown when the fetched HN item does not exist or has been removed: the Firebase API returns `null` for an unknown id, or the item carries `deleted: true` / `dead: true`. The error's resource is `hackernews/<id>` with hint 'Story not found, deleted, or dead'. This is an expected outcome for removed content, not a network failure.","triggerScenarios":"Calling `hackernews read` with a numeric but nonexistent id (HN returns null); reading a story/comment that moderators killed or the author deleted; ids of items that were never stories (e.g. poll items are handled but some item types may be absent).","commonSituations":"Re-running scripts on ids saved weeks/months earlier whose stories were since deleted; typos in an otherwise valid numeric id (e.g. 398473011 vs 39847301); scraping archived lists containing dead HN posts.","solutions":["Verify the id on huggingface — open https://news.ycombinator.com/item?id=<id> in a browser to confirm it exists","Double-check digits of the id (off-by-one typos look valid numerically)","If the story was deleted/dead, find an alternative source (e.g. a mirror or the Wayback Machine) — the CLI cannot return removed content"],"exampleFix":"// before\nopencli hackernews read 398473011   // null item -> EmptyResultError\n// after\nopencli hackernews read 39847301    // live, valid story id","handlingStrategy":"try-catch","validationCode":"// Pre-check via the public Firebase API before invoking the CLI\nconst res = await fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json`);\nconst item = await res.json();\nif (!item || item.deleted || item.dead) {\n  console.warn(`HN item ${id} is missing/deleted/dead; skipping`);\n}","typeGuard":"function isLiveStory(item) {\n  return item != null && item.deleted !== true && item.dead !== true;\n}","tryCatchPattern":"try {\n  await run(['opencli', 'hackernews', 'read', id]);\n} catch (e) {\n  if (e.name === 'EmptyResultError' || String(e.message).includes('hackernews/')) {\n    console.warn(`Story ${id} not found, deleted, or dead; skipping`);\n  } else throw e;\n}","preventionTips":["Treat HN ids as ephemeral — deleted/dead items are common and unrecoverable via the API","Pre-check items with the Firebase endpoint when batch-processing old id lists","Verify ids against news.ycombinator.com/item?id=<id> before scripting around them"],"tags":["empty-result","not-found","hn","deleted-content"],"backgroundTag":"resource-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}