Hmbown/CodeWhale · error · Error

${detail || ("HTTP " + res.status)}

Error message

${detail || ("HTTP " + res.status)}

What it means

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).

Source

Thrown at crates/tui/src/runtime_mobile.html:325

    }

    function headers(extra = {}) {
      const out = Object.assign({ "Content-Type": "application/json" }, extra);
      if (token()) out.Authorization = "Bearer " + token();
      return out;
    }

    async function api(path, options = {}) {
      const res = await fetch(path, Object.assign({}, options, {
        headers: headers(options.headers || {})
      }));
      if (!res.ok) {
        let detail = await res.text();
        try {
          const parsed = JSON.parse(detail);
          detail = parsed.error?.message || detail;
        } catch (_) {}
        throw new Error(detail || ("HTTP " + res.status));
      }
      if (res.status === 204) return null;
      return res.json();
    }

    function escapeHtml(raw) {
      return String(raw).replace(/[&<>"']/g, (char) => ({
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        "\"": "&quot;",
        "'": "&#039;"
      }[char]));
    }

    function eventPayload(data) {
      return data && typeof data === "object" && "payload" in data ? data.payload : data;
    }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Open devtools Network tab and read the actual status behind `HTTP <n>`
  2. Ensure the mobile runtime server build matches the served HTML (rebuild/reinstall the crate)
  3. Check the server log at the same timestamp for the failing request
  4. Re-authenticate if the status was 401/403

Example fix

// before
throw new Error(detail || ('HTTP ' + res.status));

// after - keep the status code even when a detail exists
throw new Error(detail ? `HTTP ${res.status}: ${detail}` : `HTTP ${res.status}`);
Defensive patterns

Strategy: try-catch

Validate before calling

async function apiReachable() {
  try {
    const res = await fetch('/v1/health');
    return res.ok;
  } catch (_) {
    return false;
  }
}

Try / catch

try {
  return await api(route, options);
} catch (err) {
  showToast(err.message || 'request failed');
  console.error('[runtime-mobile]', route, err.message);
  return null;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/49b8d22b59173cf6. Report an issue: GitHub.