{"record":{"id":"b5c2881c8e23e59c","repo":"Hmbown/CodeWhale","slug":"this-browser-session-is-not-authenticated-restart","errorCode":null,"errorMessage":"This browser session is not authenticated. Restart `codewhale web` to open a fresh one-time session.","messagePattern":"This browser session is not authenticated\\. Restart `codewhale web` to open a fresh one-time session\\.","errorType":"http","errorClass":"Error","httpStatus":401,"severity":"error","filePath":"crates/tui/src/runtime_web/app.mjs","lineNumber":712,"sourceCode":"    if (options.body != null && !headers.has(\"content-type\")) {\n      headers.set(\"content-type\", \"application/json\");\n    }\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\");","sourceCodeStart":694,"sourceCodeEnd":730,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/8880682c63083a91624de936797efa3ce9e498fd/crates/tui/src/runtime_web/app.mjs#L694-L730","documentation":"The app.mjs request wrapper special-cases 401: any 401 from the runtime API is reported as an unauthenticated browser session. `codewhale web` authenticates the browser via a one-time session; when that session no longer exists server-side (restart, expiry), every API call returns 401.","triggerScenarios":"The web runtime restarted (session store reset) while the tab stayed open; the session cookie was cleared or blocked; the one-time URL opened after expiry; same-origin credentialed requests sent from a different origin.","commonSituations":"Leaving the web UI open overnight; running `codewhale web` twice so the second instance invalidates the first session; privacy settings dropping cookies.","solutions":["Restart `codewhale web` to mint a fresh one-time session and open the URL it prints","Close stale tabs from the old session","Allow cookies for the runtime origin"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"const res = await fetch('/v1/session', { credentials: 'same-origin' });\nif (res.status === 401) {\n  showReauthNotice('Restart `codewhale web` to open a fresh one-time session.');\n  return;\n}","typeGuard":null,"tryCatchPattern":"catch (err) {\n  if (err.message.includes('not authenticated')) {\n    stopPolling();\n    showReauthNotice(err.message);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Detect 401 centrally and stop all polling instead of spraying errors","Close old tabs when restarting `codewhale web`","Run only one web session at a time per runtime instance"],"tags":["web","auth","session","runtime"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}