can1357/oh-my-pi · error
Failed to fetch item ${itemId}
Error message
Failed to fetch item ${itemId} What it means
The Hacker News special handler in the web-fetch pipeline throws this when `fetchItem()` returns null for a given `?id=` parameter. `fetchItem` returns null either because `loadPage` reported `ok: false` (network failure, HTTP error, bot-block) or because `tryParseJson` could not decode the Firebase API response body. The error is caught by handleHackerNews and surfaced as an error result block rather than crashing the tool.
Source
Thrown at packages/coding-agent/src/web/scrapers/hackernews.ts:149
}
return output;
}
export const handleHackerNews: SpecialHandler = async (url, timeout, signal) => {
const parsed = new URL(url);
if (!parsed.hostname.includes("news.ycombinator.com")) return null;
const notes: string[] = [];
let content = "";
const fetchedAt = new Date().toISOString();
try {
const itemId = parsed.searchParams.get("id");
if (itemId) {
const item = await fetchItem(parseInt(itemId, 10), timeout, signal);
if (!item) throw new Error(`Failed to fetch item ${itemId}`);
content = await renderStory(item, timeout, 0, signal);
notes.push(`Fetched HN item ${itemId} with top-level comments (depth 2)`);
} else if (parsed.pathname === "/" || parsed.pathname === "/news") {
const { content: raw, ok } = await loadPage(`${API_BASE}/topstories.json`, { timeout, signal });
if (!ok) throw new Error("Failed to fetch top stories");
const ids = tryParseJson<number[]>(raw);
if (!ids) throw new Error("Failed to parse top stories");
content = await renderListing(ids, timeout, "Hacker News - Top Stories", signal);
notes.push("Fetched top 20 stories from HN front page");
} else if (parsed.pathname === "/newest") {
const { content: raw, ok } = await loadPage(`${API_BASE}/newstories.json`, { timeout, signal });
if (!ok) throw new Error("Failed to fetch new stories");
const ids = tryParseJson<number[]>(raw);
if (!ids) throw new Error("Failed to parse new stories");
content = await renderListing(ids, timeout, "Hacker News - New Stories", signal);
notes.push("Fetched top 20 new stories");
} else if (parsed.pathname === "/best") {View on GitHub (pinned to 9690622007)
Solutions
- Verify network connectivity to hacker-news.firebaseio.com and retry; transient failures are the most common cause.
- Check that the `id` query parameter is a valid numeric HN item id (Firebase returns null for unknown ids).
- Check the notes field of the returned result block for the underlying transport error reported by loadPage.
- If Firebase is down, fetch the item via https://hn.algolia.com/api/v1/items/<id> as an alternative API.
- Increase the tool's timeout if the failure was a timeout-induced ok:false.
Example fix
// before (caller treating error result as hard failure)
const res = await webFetch("https://news.ycombinator.com/item?id=999999999");
if (!res.ok) throw new Error(res.error);
// after: check notes and validate id before fetching
const id = new URL(url).searchParams.get("id");
if (!id || !/^\d+$/.test(id)) throw new Error(`invalid HN item id: ${id}`);
const res = await webFetch(url);
if (!res.ok) logger.warn("HN fetch failed", { notes: res.notes }); Defensive patterns
Strategy: validation
Validate before calling
const id = new URL(url).searchParams.get("id");
if (!id || !/^\d+$/.test(id)) throw new Error(`invalid HN item id: ${id}`); Type guard
function isHnItem(item: unknown): item is { id: number; title?: string } {
return typeof item === "object" && item !== null && "id" in item && typeof (item as { id: unknown }).id === "number";
} Try / catch
try {
const res = await webFetch(hnUrl);
} catch (err) {
if (err instanceof ToolAbortError) throw err;
logger.warn("HN item fetch failed, check network/Firebase", { url: hnUrl, err });
} Prevention
- Validate the `id` query parameter is numeric before fetching.
- Pre-probe hacker-news.firebaseio.com health before batch HN fetches.
- Read the result block's notes field for underlying loadPage transport errors.
- Have an Algolia HN API fallback ready.
- Set a realistic timeout; HN Firebase is usually fast, so long delays indicate network problems.
When it happens
Trigger: Calling the web-fetch tool with a news.ycombinator.com item URL (e.g. https://news.ycombinator.com/item?id=12345) where the GET to https://hacker-news.firebaseio.com/v0/item/<id>.json fails or returns a non-JSON body. Also fires for a non-numeric id parse (parseInt yields NaN, Firebase 404s) or a deleted/deleted-unknown item id.
Common situations: Offline or DNS-broken environment; HN Firebase API rate-limiting or outage; corporate proxy stripping the response; malformed/nonexistent item id (e.g. trailing garbage in id param); very old or deleted item that returns literal `null` from Firebase, which tryParseJson decodes to null.
Related errors
- ${response.status} ${response.statusText}: ${text}
- Failed to fetch top stories
- Failed to fetch new stories
- Failed to fetch best stories
- HTTP error ${res.status} on ${endpoint}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/cd3662132eb732d5.
Report an issue: GitHub.