Hmbown/CodeWhale · error

${message}

Error message

${message}

What it means

Generic failure of the browser client's api() wrapper: a fetch to the Runtime web API returned a non-OK HTTP status whose body text (or parsed JSON message) becomes the thrown error message. A 401 is upgraded to a friendly message telling the user their one-time browser session is no longer authenticated.

Solutions

  1. On the 401 variant, restart `codewhale web` and open the freshly printed one-time session URL in the same browser.
  2. Retry the action after confirming the Runtime process is still running and was not restarted since page load.
  3. If the message shows a 404/500, check Runtime logs for the failing endpoint and align the browser client and Runtime versions.

Example fix

// before: bookmarked stale session URL gets 401
fetch("/v1/providers")
// after: always launch via the fresh URL from `codewhale web`
// $ codewhale web
// Listening on http://127.0.0.1:PORT/?session=<one-time-token>
Defensive patterns

Strategy: try-catch

Validate before calling

const res = await fetch('/v1/providers', { headers: authHeaders() });
if (res.status === 401) throw new Error('session expired — restart codewhale web');
if (!res.ok) throw new Error(`runtime api ${res.status}`);

Type guard

function isAuthError(err) {
  return err instanceof Error && err.message.includes('not authenticated');
}

Try / catch

try {
  data = await api('/v1/providers');
} catch (e) {
  if (isAuthError(e)) showSessionExpiredDialog();
  else showSnackbar(String(e.message));
}

Prevention

When it happens

Trigger: Any fetch from app.mjs via api() where response.ok is false — e.g. 401 after the one-time session token is consumed/expired, 404 on a stale endpoint, or 500 from a Runtime error.

Common situations: Bookmarking the web UI and reopening it later after the one-time session expired; sharing the URL with a second browser; Runtime restarted between page load and an API call; a version-mismatched Runtime missing an endpoint.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/9b8286b1856615ed. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/runtime_web/app.mjs:955

    }
    const response = await fetch(path, {
      ...options,
      headers,
      credentials: "same-origin",
      cache: "no-store",
    });
    if (!response.ok) {
      let message = `${response.status} ${response.statusText}`.trim();
      try {
        const body = await response.json();
        message = body?.error?.message || body?.message || message;
      } catch (_error) {
        // The status line is enough when the response is not JSON.
      }
      if (response.status === 401) {
        message = "This browser session is not authenticated. Restart `codewhale web` to open a fresh one-time session.";
      }
      throw new Error(message);
    }
    if (response.status === 204) return null;
    const contentType = response.headers.get("content-type") || "";
    return contentType.includes("application/json") ? response.json() : response.text();
  }

  function renderThreadList() {
    dom.threadList.replaceChildren();
    if (app.summaries.length === 0) {
      const empty = element("p", "thread-preview", "No matching threads");
      empty.style.padding = "8px 10px";
      dom.threadList.append(empty);
      return;
    }

    const groups = groupThreadSummaries(app.summaries);
    if (groups.needsYou.length > 0) {
      appendThreadGroup("Needs you", "needs-you", groups.needsYou);

View on GitHub (pinned to 433685b202)