{"record":{"id":"989ec95552bd6a05","repo":"odysseus-dev/odysseus","slug":"detail-http-res-status-989ec9","errorCode":null,"errorMessage":"detail || `HTTP ${res.status}`","messagePattern":"detail \\|\\| `HTTP (.+?)`","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/research/panel.js","lineNumber":1226,"sourceCode":"      setTimeout(() => { btn.innerHTML = orig; }, 2000);\n    }\n  }\n}\n\n// ── Chat about this research (server-side spinoff) ──\n\nasync function _chatAboutResearch(researchId, btn) {\n  if (!researchId) return;\n  const origLabel = btn ? btn.innerHTML : '';\n  if (btn) { btn.disabled = true; btn.innerHTML = `${_chatIcon} Creating…`; }\n  try {\n    const res = await fetch(`${_apiBase}/api/research/spinoff/${researchId}`, {\n      method: 'POST', credentials: 'same-origin',\n    });\n    if (!res.ok) {\n      let detail = '';\n      try { detail = (await res.json()).detail || ''; } catch {}\n      throw new Error(detail || `HTTP ${res.status}`);\n    }\n    const payload = await res.json();\n    if (_sessionModule && _sessionModule.selectSession && payload.session_id) {\n      if (_sessionModule.loadSessions) await _sessionModule.loadSessions().catch(() => {});\n      await _sessionModule.selectSession(payload.session_id);\n      closePanel();\n    } else if (payload.session_id) {\n      window.location.hash = '#' + payload.session_id;\n      window.location.reload();\n    } else {\n      // 200 OK but no session_id — server contract violation. Don't leave\n      // the button stuck on 'Creating…'; surface the failure instead.\n      throw new Error('Server returned no session id');\n    }\n  } catch (e) {\n    if (btn) { btn.disabled = false; btn.innerHTML = origLabel; }\n    alert('Could not start follow-up chat: ' + e.message);\n  }","sourceCodeStart":1208,"sourceCodeEnd":1244,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/research/panel.js#L1208-L1244","documentation":"Thrown by _chatAboutResearch in research/panel.js when POST /api/research/spinoff/{researchId} fails. The client attempts to extract a FastAPI-style detail from the JSON body; if absent or unparseable it falls back to the bare HTTP status. The catch re-enables the button and alerts, so UI state is restored.","triggerScenarios":"Creating a follow-up chat from a research report whose id no longer exists (404); research data deleted between panel open and click; server cannot create the spinoff session (db error → 500 with detail); auth expired (401 with empty body → 'HTTP 401').","commonSituations":"Stale research panel left open while the server restarted or the research store was cleared; long-lived tabs with expired sessions.","solutions":["Close and reopen the research panel to refresh ids, then retry.","Read the detail in the alert for the server's reason; check server logs for 5xx.","Re-authenticate if the status is 401.","Verify the research entry still exists in the panel list."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (!researchId) { btn.disabled = false; return; }","typeGuard":"const isSpinoffPayload = (p) => !!p && !!p.session_id;","tryCatchPattern":"try { ... } catch (e) { btn.disabled = false; btn.innerHTML = origLabel; alert('Could not start follow-up chat: ' + e.message); }","preventionTips":["Restore button state in a finally-style catch as this code does","Extract detail from JSON before falling back to status","Refresh research ids when the panel regains focus"],"tags":["research","http","session","spinoff"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}