{"record":{"id":"679c8ccdc78cf4d1","repo":"odysseus-dev/odysseus","slug":"statustext","errorCode":null,"errorMessage":"statusText","messagePattern":"statusText","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/documentLibrary.js","lineNumber":2692,"sourceCode":"    let _researchSelectMode = false;\n    let _researchArchivedView = false;\n    const _researchSelected = new Set();\n\n    async function _renderLibResearch() {\n      const grid = document.getElementById('doclib-research-grid');\n      const stats = document.getElementById('doclib-research-stats');\n      if (!grid) return;\n      // Show our whirlpool spinner instead of the plain \"Loading...\" text.\n      grid.innerHTML = '';\n      try {\n        const _spm = (await import('./spinner.js')).default;\n        const _sp = _spm.createWhirlpool(22);\n        _sp.element.style.cssText = 'margin:18px auto;display:block;';\n        grid.appendChild(_sp.element);\n      } catch { grid.innerHTML = '<div class=\"hwfit-loading\">Loading…</div>'; }\n      try {\n        const res = await fetch('/api/research/library' + (_researchArchivedView ? '?archived=true' : ''), { credentials: 'same-origin' });\n        if (!res.ok) throw new Error(res.statusText);\n        const data = await res.json();\n        _researchItems = data.research || data || [];\n      } catch (e) {\n        grid.innerHTML = `<div class=\"hwfit-loading\">Failed to load: ${_esc(e.message)}</div>`;\n        return;\n      }\n      _renderResearchGrid();\n    }\n\n    // Toggle inline preview for a research row. Mirrors _toggleChatPreview\n     // but pulls research-specific metadata: query, sources list (truncated),\n     // followed by an \"Open\" action that loads the full report.\n    async function _toggleResearchPreview(card, item) {\n      const preview = card.querySelector('.doclib-chat-preview');\n      if (!preview) return;\n      const isOpen = card.classList.contains('doclib-card-expanded');\n      const grid = card.closest('.doclib-grid');\n      if (grid) {","sourceCodeStart":2674,"sourceCodeEnd":2710,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/documentLibrary.js#L2674-L2710","documentation":"Thrown when the fetch to GET /api/research/library returns a non-2xx status. The code throws new Error(res.statusText), so the message is whatever the server's status line carries. Note that over HTTP/2 (and many proxies) statusText is an empty string, so the surfaced message can literally be blank.","triggerScenarios":"GET /api/research/library (optionally ?archived=true) responding with 4xx/5xx — e.g. 401 after session expiry, 500 if the research store fails to load, or a dev-server 404 when the API route is not mounted.","commonSituations":"Expired auth cookie on a long-open tab; backend restarted and lost state; running the static frontend against a server that does not expose the research API; HTTP/2 yielding empty statusText so users see 'Failed to load: ' with no reason.","solutions":["Inspect the actual status code (res.status) and response body in DevTools Network tab to identify why /api/research/library failed","Include res.status in the thrown message so it is never blank: throw new Error(`HTTP ${res.status}`)","If the server returns a JSON error body, parse it and prefer its detail/error field over statusText","If 401/403, re-authenticate or reload the session before retrying the library load"],"exampleFix":"// before\nif (!res.ok) throw new Error(res.statusText);\n\n// after\nif (!res.ok) {\n  let msg = `HTTP ${res.status}`;\n  try { const b = await res.json(); msg = b.detail || b.error || msg; } catch {}\n  throw new Error(msg);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const res = await fetch(url, {credentials:'same-origin'}); if (!res.ok) { let m = `HTTP ${res.status}`; try { m = (await res.json()).detail || m; } catch {} throw new Error(m); } ... } catch (e) { grid.innerHTML = `<div class=\"hwfit-loading\">Failed to load: ${_esc(e.message)}</div>`; }","preventionTips":["Never use bare res.statusText as the only message — it is empty over HTTP/2","Always include res.status in fallback error messages","Parse the JSON error body (detail/error) before falling back","Handle 401 with a session-refresh path instead of showing a raw failure"],"tags":["network","http","fetch","research-library","status-code"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}