{"record":{"id":"49b8d22b59173cf6","repo":"Hmbown/CodeWhale","slug":"detail-http-res-status","errorCode":null,"errorMessage":"${detail || (\"HTTP \" + res.status)}","messagePattern":"\\$\\{detail \\|\\| \\(\"HTTP \" \\+ res\\.status\\)\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_mobile.html","lineNumber":325,"sourceCode":"    }\n\n    function headers(extra = {}) {\n      const out = Object.assign({ \"Content-Type\": \"application/json\" }, extra);\n      if (token()) out.Authorization = \"Bearer \" + token();\n      return out;\n    }\n\n    async function api(path, options = {}) {\n      const res = await fetch(path, Object.assign({}, options, {\n        headers: headers(options.headers || {})\n      }));\n      if (!res.ok) {\n        let detail = await res.text();\n        try {\n          const parsed = JSON.parse(detail);\n          detail = parsed.error?.message || detail;\n        } catch (_) {}\n        throw new Error(detail || (\"HTTP \" + res.status));\n      }\n      if (res.status === 204) return null;\n      return res.json();\n    }\n\n    function escapeHtml(raw) {\n      return String(raw).replace(/[&<>\"']/g, (char) => ({\n        \"&\": \"&amp;\",\n        \"<\": \"&lt;\",\n        \">\": \"&gt;\",\n        \"\\\"\": \"&quot;\",\n        \"'\": \"&#039;\"\n      }[char]));\n    }\n\n    function eventPayload(data) {\n      return data && typeof data === \"object\" && \"payload\" in data ? data.payload : data;\n    }","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/runtime_mobile.html#L307-L343","documentation":"Client-side api() helper in the mobile runtime page: every non-2xx response becomes an Error whose message is the body's error.message (parsed from JSON), else the raw body; when the body is empty the message degrades to `HTTP <status>` (HTTP/2 responses have no statusText).","triggerScenarios":"Calling /v1/... runtime endpoints (threads, turns, steer, interrupt) when the server responds non-2xx with an empty body - unknown route after a version mismatch, a bare 500, or an interposing proxy.","commonSituations":"runtime_mobile.html shipped by a codewhale build older/newer than the API it targets; reverse proxies returning empty 502/504; runtime restarting mid-request.","solutions":["Open devtools Network tab and read the actual status behind `HTTP <n>`","Ensure the mobile runtime server build matches the served HTML (rebuild/reinstall the crate)","Check the server log at the same timestamp for the failing request","Re-authenticate if the status was 401/403"],"exampleFix":"// before\nthrow new Error(detail || ('HTTP ' + res.status));\n\n// after - keep the status code even when a detail exists\nthrow new Error(detail ? `HTTP ${res.status}: ${detail}` : `HTTP ${res.status}`);","handlingStrategy":"try-catch","validationCode":"async function apiReachable() {\n  try {\n    const res = await fetch('/v1/health');\n    return res.ok;\n  } catch (_) {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await api(route, options);\n} catch (err) {\n  showToast(err.message || 'request failed');\n  console.error('[runtime-mobile]', route, err.message);\n  return null;\n}","preventionTips":["Keep the served runtime_mobile.html and the API server from the same build","Surface HTTP status codes in client error messages for faster triage","Handle auth prompts before queuing API calls on page load"],"tags":["web","http","client","runtime"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}