{"record":{"id":"f1beaf15b1327742","repo":"odysseus-dev/odysseus","slug":"await-res-text-f1beaf","errorCode":null,"errorMessage":"await res.text()","messagePattern":"await res\\.text\\(\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/documentLibrary.js","lineNumber":2820,"sourceCode":"        e.stopPropagation();\n        const a = document.createElement('a');\n        a.href = '/api/research/report/' + item.id;\n        a.target = '_blank';\n        a.rel = 'noopener';\n        document.body.appendChild(a);\n        a.click();\n        a.remove();\n      });\n      const delBtn = preview.querySelector('.doclib-chat-delete-btn');\n      if (delBtn) delBtn.addEventListener('click', async (e) => {\n        e.stopPropagation();\n        const ok = uiModule && uiModule.styledConfirm\n          ? await uiModule.styledConfirm('Delete this research report?', { confirmText: 'Delete', danger: true })\n          : window.confirm('Delete this research report?');\n        if (!ok) return;\n        try {\n          const res = await fetch(`${API_BASE}/api/research/${item.id}`, { method: 'DELETE', credentials: 'same-origin' });\n          if (!res.ok) throw new Error(await res.text());\n          if (item.archived) {\n            _renderLibArchive();\n          } else {\n            _researchItems = _researchItems.filter(r => r.id !== item.id);\n            _renderResearchGrid();\n          }\n        } catch (err) {\n          if (uiModule && uiModule.showError) uiModule.showError('Failed to delete: ' + err.message);\n        }\n      });\n      const arcBtn = preview.querySelector('.doclib-chat-archive-btn');\n      if (arcBtn) arcBtn.addEventListener('click', async (e) => {\n        e.stopPropagation();\n        // From the main Archive tab the item is already archived → Restore and\n        // refresh the archive. From the Research tab, toggle as before.\n        const fromArchiveTab = !!item.archived;\n        const toArchived = fromArchiveTab ? false : !_researchArchivedView;\n        try {","sourceCodeStart":2802,"sourceCodeEnd":2838,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/documentLibrary.js#L2802-L2838","documentation":"Thrown when DELETE /api/research/{item.id} returns a non-2xx status; the entire raw response body is used as the Error message. If the server answers with an HTML error page (common for proxy 502/504 or unhandled exceptions), the user-visible toast 'Failed to delete: ...' contains a wall of markup.","triggerScenarios":"Clicking Delete in a research report preview (after confirming the styled dialog) and the DELETE failing: 404 when item.id is stale, 401 after session expiry, 409/500 when the storage layer cannot remove the report.","commonSituations":"Item already deleted in another tab; backend restarted with an in-memory store so every id 404s; proxy-level error pages producing noisy toasts because res.text() is thrown verbatim.","solutions":["Look at the DELETE /api/research/{id} response in DevTools to get the real status and reason","If 404, the list is stale — reload the research grid and the item will be gone","Prefer a parsed, length-limited message over raw body text: throw new Error(`HTTP ${res.status}`) with optional JSON detail","On success also handle the archived branch correctly (already present) so the grid re-renders"],"exampleFix":"// before\nif (!res.ok) throw new Error(await res.text());\n\n// after\nif (!res.ok) {\n  let msg = `HTTP ${res.status}`;\n  try { msg = (await res.json()).detail || msg; } catch {}\n  throw new Error(msg);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const res = await fetch(url, {method:'DELETE', credentials:'same-origin'}); if (res.status === 404) { /* already gone — treat as success */ } else if (!res.ok) { let m = `HTTP ${res.status}`; try { m=(await res.json()).detail||m; } catch {} throw new Error(m); } ... } catch (err) { uiModule?.showError('Failed to delete: ' + err.message); }","preventionTips":["Never throw raw res.text() — HTML error pages leak markup into toasts","Treat 404 on DELETE as idempotent success when the list can be refreshed","Re-render the grid from server state after deletions instead of local filtering only","Confirm destructive actions (already done here) so accidental double-deletes surface as 404s not errors"],"tags":["network","http","fetch","delete","research","error-hygiene"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}