{"record":{"id":"0332de9c9101c564","repo":"Hmbown/CodeWhale","slug":"response-status-response-statustext","errorCode":null,"errorMessage":"${response.status} ${response.statusText}","messagePattern":"\\$\\{response\\.status\\} \\$\\{response\\.statusText\\}","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":714,"sourceCode":"    }\n    const response = await fetch(path, {\n      ...options,\n      headers,\n      credentials: \"same-origin\",\n      cache: \"no-store\",\n    });\n    if (!response.ok) {\n      let message = `${response.status} ${response.statusText}`.trim();\n      try {\n        const body = await response.json();\n        message = body?.error?.message || body?.message || message;\n      } catch (_error) {\n        // The status line is enough when the response is not JSON.\n      }\n      if (response.status === 401) {\n        message = \"This browser session is not authenticated. Restart `codewhale web` to open a fresh one-time session.\";\n      }\n      throw new Error(message);\n    }\n    if (response.status === 204) return null;\n    const contentType = response.headers.get(\"content-type\") || \"\";\n    return contentType.includes(\"application/json\") ? response.json() : response.text();\n  }\n\n  function renderThreadList() {\n    dom.threadList.replaceChildren();\n    if (app.summaries.length === 0) {\n      const empty = element(\"p\", \"thread-preview\", \"No matching threads\");\n      empty.style.padding = \"8px 10px\";\n      dom.threadList.append(empty);\n      return;\n    }\n    for (const summary of app.summaries) {\n      const row = element(\"button\", \"thread-row\");\n      row.type = \"button\";\n      row.dataset.threadId = summary.id;","sourceCodeStart":696,"sourceCodeEnd":732,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/runtime_web/app.mjs#L696-L732","documentation":"In the desktop web app (runtime_web/app.mjs), a failed fetch becomes an Error with message `${status} ${statusText}`. That literal surfaces only when the error body is not JSON or carries neither error.message nor message - e.g. HTML error pages, empty bodies, or plain text from proxies.","triggerScenarios":"The runtime server returns 5xx with an HTML/empty body; a gateway answers 502/504; the requested route does not exist in the running build (404 with empty body).","commonSituations":"`codewhale web` crashed or is restarting while the browser keeps polling; version skew between app.mjs and the served API; corporate proxies rewriting responses.","solutions":["Confirm the codewhale web process is alive and re-open its session URL","Check the server log at the same timestamp","If behind a proxy, bypass it or raise its timeouts"],"exampleFix":"// before\ntry {\n  const body = await response.json();\n  message = body?.error?.message || body?.message || message;\n} catch (_error) {\n  // The status line is enough when the response is not JSON.\n}\n\n// after - clone first so the raw non-JSON body can still be logged\nconst rawClone = response.clone();\ntry {\n  const body = await response.json();\n  message = body?.error?.message || body?.message || message;\n} catch (_error) {\n  console.warn('non-JSON error body:', await rawClone.text().catch(() => ''));\n}","handlingStrategy":"try-catch","validationCode":"const res = await fetch(route, { credentials: 'same-origin', cache: 'no-store' });\nif (!res.ok && !(res.headers.get('content-type') || '').includes('application/json')) {\n  console.error('non-JSON error from', route, res.status);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await requestJson(route, options);\n} catch (err) {\n  const m = /^(\\d{3}) /.exec(err.message);\n  if (m && Number(m[1]) >= 500) { scheduleReconnect(); return null; }\n  showError(err.message);\n  throw err;\n}","preventionTips":["Clone error responses before parsing so raw bodies remain loggable","Keep the web app bundle and the API server version-locked","Add liveness polling to detect a dead runtime before user actions fail"],"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"}