{"record":{"id":"c190206c3b12e685","repo":"can1357/oh-my-pi","slug":"failed-to-fetch-best-stories","errorCode":null,"errorMessage":"Failed to fetch best stories","messagePattern":"Failed to fetch best stories","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/web/scrapers/hackernews.ts","lineNumber":169,"sourceCode":"\t\t\tcontent = await renderStory(item, timeout, 0, signal);\n\t\t\tnotes.push(`Fetched HN item ${itemId} with top-level comments (depth 2)`);\n\t\t} else if (parsed.pathname === \"/\" || parsed.pathname === \"/news\") {\n\t\t\tconst { content: raw, ok } = await loadPage(`${API_BASE}/topstories.json`, { timeout, signal });\n\t\t\tif (!ok) throw new Error(\"Failed to fetch top stories\");\n\t\t\tconst ids = tryParseJson<number[]>(raw);\n\t\t\tif (!ids) throw new Error(\"Failed to parse top stories\");\n\t\t\tcontent = await renderListing(ids, timeout, \"Hacker News - Top Stories\", signal);\n\t\t\tnotes.push(\"Fetched top 20 stories from HN front page\");\n\t\t} else if (parsed.pathname === \"/newest\") {\n\t\t\tconst { content: raw, ok } = await loadPage(`${API_BASE}/newstories.json`, { timeout, signal });\n\t\t\tif (!ok) throw new Error(\"Failed to fetch new stories\");\n\t\t\tconst ids = tryParseJson<number[]>(raw);\n\t\t\tif (!ids) throw new Error(\"Failed to parse new stories\");\n\t\t\tcontent = await renderListing(ids, timeout, \"Hacker News - New Stories\", signal);\n\t\t\tnotes.push(\"Fetched top 20 new stories\");\n\t\t} else if (parsed.pathname === \"/best\") {\n\t\t\tconst { content: raw, ok } = await loadPage(`${API_BASE}/beststories.json`, { timeout, signal });\n\t\t\tif (!ok) throw new Error(\"Failed to fetch best stories\");\n\t\t\tconst ids = tryParseJson<number[]>(raw);\n\t\t\tif (!ids) throw new Error(\"Failed to parse best stories\");\n\t\t\tcontent = await renderListing(ids, timeout, \"Hacker News - Best Stories\", signal);\n\t\t\tnotes.push(\"Fetched top 20 best stories\");\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\n\t\treturn buildResult(content, { url, method: \"hackernews\", fetchedAt, notes });\n\t} catch (err) {\n\t\tconst errorMsg = err instanceof Error ? err.message : String(err);\n\t\tnotes.push(`Error: ${errorMsg}`);\n\t\treturn buildResult(`# Error fetching Hacker News content\\n\\n${errorMsg}`, {\n\t\t\turl,\n\t\t\tmethod: \"hackernews\",\n\t\t\tfetchedAt,\n\t\t\tnotes,\n\t\t});","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/scrapers/hackernews.ts#L151-L187","documentation":"Thrown by handleHackerNews when `loadPage` returns `ok: false` for the Firebase `beststories.json` endpoint while handling https://news.ycombinator.com/best. Identical mechanism to the other listing endpoints: transport failure, non-2xx status, or unreadable body after the user-agent rotation in loadPage.","triggerScenarios":"Calling web-fetch on https://news.ycombinator.com/best when GET https://hacker-news.firebaseio.com/v0/beststories.json fails — network outage, DNS failure, Firebase error, blocked user agents, or timeout.","commonSituations":"Firebase API outage; offline environment; DNS failure; rate limiting; firewall blocking Firebase; slow network exceeding timeout.","solutions":["Verify connectivity to hacker-news.firebaseio.com and retry.","Inspect the notes/error in the returned result block for the underlying transport error.","Use Algolia HN API as a best-stories fallback.","Increase the timeout for slow networks.","Check proxy/firewall rules for Firebase hosting domains."],"exampleFix":"// before\nconst res = await webFetch(\"https://news.ycombinator.com/best\");\n// after\nconst health = await fetch(\"https://hacker-news.firebaseio.com/v0/beststories.json\", { method: \"HEAD\" });\nif (!health.ok) return algoliaBestFallback();\nconst res = await webFetch(\"https://news.ycombinator.com/best\");","handlingStrategy":"retry","validationCode":"const probe = await fetch(\"https://hacker-news.firebaseio.com/v0/beststories.json\", { method: \"HEAD\" });\nif (!probe.ok) throw new Error(`HN Firebase unreachable: ${probe.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const res = await webFetch(\"https://news.ycombinator.com/best\");\n} catch (err) {\n  if (err instanceof ToolAbortError) throw err;\n  // fall back to an Algolia best/top query\n}","preventionTips":["HEAD-probe beststories.json before fetching /best.","Retry transient failures with a short backoff.","Confirm outbound HTTPS to Firebase hosting is allowed by the firewall.","Keep an Algolia fallback for best stories.","Tune timeout for the network's baseline latency."],"tags":["network","http","hacker-news","firebase"],"backgroundTag":"fetch-failed-non-ok-response","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}