{"record":{"id":"fd47042fbf6a52ad","repo":"can1357/oh-my-pi","slug":"url-res-status","errorCode":null,"errorMessage":"${url}: ${res.status}","messagePattern":"\\$\\{url\\}: \\$\\{res\\.status\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/web/app.tsx","lineNumber":117,"sourceCode":"\ttool?: string;\n\tisError?: boolean;\n\ttext?: string;\n\ttools?: string[];\n}\n\n// ── helpers ──────────────────────────────────────────────────────────────────\n\nconst fmtUsd = (v: number) => (v >= 100 ? `$${v.toFixed(0)}` : v >= 1 ? `$${v.toFixed(2)}` : `$${v.toFixed(3)}`);\nconst fmtMin = (ms: number) => `${(ms / 60000).toFixed(1)}m`;\nconst fmtEta = (etaMs: number | null) => {\n\tif (etaMs === null) return \"—\";\n\tconst mins = Math.max(0, Math.round((etaMs - Date.now()) / 60000));\n\treturn mins >= 90 ? `~${(mins / 60).toFixed(1)}h` : `~${mins}m`;\n};\n\nasync function getJson<T>(url: string): Promise<T> {\n\tconst res = await fetch(url);\n\tif (!res.ok) throw new Error(`${url}: ${res.status}`);\n\treturn (await res.json()) as T;\n}\n\nfunction useHashRoute(): string {\n\tconst [hash, setHash] = useState(location.hash || \"#/\");\n\tuseEffect(() => {\n\t\tconst onChange = () => setHash(location.hash || \"#/\");\n\t\twindow.addEventListener(\"hashchange\", onChange);\n\t\treturn () => window.removeEventListener(\"hashchange\", onChange);\n\t}, []);\n\treturn hash;\n}\n\n/** Poll a JSON endpoint on an interval (SSE covers the run list; details poll).\n *  Returns the latest payload plus a manual refresh for after mutations. */\nfunction usePolled<T>(url: string | null, intervalMs: number): [T | null, () => void] {\n\tconst [data, setData] = useState<T | null>(null);\n\tconst [nonce, setNonce] = useState(0);","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/web/app.tsx#L99-L135","documentation":"getJson is the metaharness web app's fetch helper: it performs a GET and, if the response is not ok, throws an Error formatted as \"<url>: <status>\". It surfaces any non-2xx HTTP status (404 missing route, 500 server error, etc.) to the React load path.","triggerScenarios":"Any fetch in the dashboard to a URL returning a non-OK status — endpoint not mounted on the server, wrong port/hash route, API handler crashing, or auth/proxy rejection.","commonSituations":"Dashboard open against a stale server version whose endpoints moved; server restarted mid-session; reverse proxy returning 502; typo'd API path.","solutions":["Check the status code in the message: 404 means wrong path/server version, 5xx means server-side failure","Confirm the metaharness server is running and the dashboard URL/port matches it","Inspect server logs for the failing request handler","Refresh the dashboard to a version matching the running server"],"exampleFix":"// before\nconst data = await getJson(\"/api/experiments/old-endpoint\");\n// after: handle failures gracefully in the UI\ntry {\n  const data = await getJson(\"/api/experiments\");\n} catch (e) {\n  setError(String(e));\n}","handlingStrategy":"try-catch","validationCode":"const res = await fetch(url, { method: \"HEAD\" });\nif (!res.ok) {\n  console.warn(`API endpoint ${url} unavailable (HTTP ${res.status}); refresh the server?`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const data = await getJson<T>(url);\n} catch (err) {\n  const m = /^(.+?): (\\d+)$/.exec(String(err.message));\n  if (m) {\n    const [, u, status] = m;\n    if (status === \"404\") showError(`Endpoint ${u} not found — server version mismatch`);\n    else showError(`Request to ${u} failed (HTTP ${status})`);\n    return fallbackData;\n  }\n  throw err;\n}","preventionTips":["Keep the dashboard and server versions in lock-step (same build/deploy)","Check the server is running and reachable before loading the dashboard","Render a graceful error state instead of crashing the route component"],"tags":["http","fetch","web"],"backgroundTag":"http-request-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}