{"record":{"id":"a5372051d6b5b638","repo":"odysseus-dev/odysseus","slug":"delete-failed","errorCode":null,"errorMessage":"Delete failed","messagePattern":"Delete failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"static/js/document.js","lineNumber":9809,"sourceCode":"    a.href = URL.createObjectURL(blob);\n    a.download = baseName + '.docx';\n    a.click();\n    URL.revokeObjectURL(a.href);\n    if (uiModule) uiModule.showToast('Exported as DOCX');\n  }\n\n  /** Delete the active document */\n  async function deleteActiveDocument() {\n    if (!activeDocId) return;\n    const doc = docs.get(activeDocId);\n    const name = doc ? doc.title : 'this document';\n    const ok = uiModule && uiModule.styledConfirm\n      ? await uiModule.styledConfirm(`Delete \"${name}\"?`, { confirmText: 'Delete', danger: true })\n      : confirm(`Delete \"${name}\"?`);\n    if (!ok) return;\n    try {\n      const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' });\n      if (!res.ok) throw new Error('Delete failed');\n      // Remove tab\n      const tab = document.querySelector(`.doc-tab[data-doc-id=\"${activeDocId}\"]`);\n      if (tab) tab.remove();\n      docs.delete(activeDocId);\n      // Switch to another doc or close panel\n      const remaining = Array.from(docs.keys());\n      if (remaining.length > 0) {\n        switchToDoc(remaining[0]);\n      } else {\n        activeDocId = null;\n        closePanel();\n      }\n      if (uiModule) uiModule.showToast('Document deleted');\n    } catch (e) {\n      console.error('Failed to delete document:', e);\n      if (uiModule) uiModule.showError('Failed to delete document');\n    }\n  }","sourceCodeStart":9791,"sourceCodeEnd":9827,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/document.js#L9791-L9827","documentation":"Thrown by deleteActiveDocument in static/js/document.js when DELETE /api/document/<activeDocId> returns non-2xx, after the user already confirmed deletion. The tab is only removed on success, so a failed delete leaves the document in the UI and in the docs map — a UI/server state divergence risk if the server actually deleted it.","triggerScenarios":"Deleting a doc that was already removed server-side (404 — unlike saveDocument there is no 404 special-case here); expired auth (401); DB constraint or server error (500). Note the fetch omits credentials, which can itself cause 401 if the API requires the session cookie.","commonSituations":"Double-delete (two tabs, or confirm twice quickly); deployments where DELETE requires credentials and this call's missing credentials option becomes a bug.","solutions":["Add credentials: 'same-origin' to the DELETE fetch to match every other call in the module.","Check the status: 404 → treat as already-deleted and remove the tab anyway; 5xx → keep the tab and surface the error.","Reconcile with the Library view after any failed delete."],"exampleFix":"// before\nconst res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' });\nif (!res.ok) throw new Error('Delete failed');\n\n// after\nconst res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE', credentials: 'same-origin' });\nif (!res.ok && res.status !== 404) {\n  let detail = '';\n  try { const j = await res.json(); detail = j?.detail || ''; } catch (_) {}\n  throw new Error(`Delete failed: HTTP ${res.status}${detail ? ` — ${detail}` : ''}`);\n}","handlingStrategy":"try-catch","validationCode":"if (!activeDocId) return; // nothing to delete\nconst ok = await confirmDelete();\nif (!ok) return;","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE', credentials: 'same-origin' });\n  if (!res.ok && res.status !== 404) throw new Error(`Delete failed: HTTP ${res.status}`);\n  removeTabAndSwitch(activeDocId); // 404 = already gone; clean up UI either way\n} catch (e) {\n  if (uiModule) uiModule.showError(`Could not delete document: ${e.message}`);\n}","preventionTips":["Always pass credentials: 'same-origin' on DELETE — this call site omits it and can 401 on cookie-auth deployments.","Treat 404 as success for idempotent delete and still clean up local UI state.","Prevent double-confirm/double-delete races by disabling the trigger while the request is in flight."],"tags":["http","delete","credentials","state-divergence"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}