odysseus-dev/odysseus · error · Error
Delete failed
Error message
Delete failed
What it means
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.
Source
Thrown at static/js/document.js:9809
a.href = URL.createObjectURL(blob);
a.download = baseName + '.docx';
a.click();
URL.revokeObjectURL(a.href);
if (uiModule) uiModule.showToast('Exported as DOCX');
}
/** Delete the active document */
async function deleteActiveDocument() {
if (!activeDocId) return;
const doc = docs.get(activeDocId);
const name = doc ? doc.title : 'this document';
const ok = uiModule && uiModule.styledConfirm
? await uiModule.styledConfirm(`Delete "${name}"?`, { confirmText: 'Delete', danger: true })
: confirm(`Delete "${name}"?`);
if (!ok) return;
try {
const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Delete failed');
// Remove tab
const tab = document.querySelector(`.doc-tab[data-doc-id="${activeDocId}"]`);
if (tab) tab.remove();
docs.delete(activeDocId);
// Switch to another doc or close panel
const remaining = Array.from(docs.keys());
if (remaining.length > 0) {
switchToDoc(remaining[0]);
} else {
activeDocId = null;
closePanel();
}
if (uiModule) uiModule.showToast('Document deleted');
} catch (e) {
console.error('Failed to delete document:', e);
if (uiModule) uiModule.showError('Failed to delete document');
}
}View on GitHub (pinned to f9235ebbf1)
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.
Example fix
// before
const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Delete failed');
// after
const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE', credentials: 'same-origin' });
if (!res.ok && res.status !== 404) {
let detail = '';
try { const j = await res.json(); detail = j?.detail || ''; } catch (_) {}
throw new Error(`Delete failed: HTTP ${res.status}${detail ? ` — ${detail}` : ''}`);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!activeDocId) return; // nothing to delete const ok = await confirmDelete(); if (!ok) return;
Try / catch
try {
const res = await fetch(`${API_BASE}/api/document/${activeDocId}`, { method: 'DELETE', credentials: 'same-origin' });
if (!res.ok && res.status !== 404) throw new Error(`Delete failed: HTTP ${res.status}`);
removeTabAndSwitch(activeDocId); // 404 = already gone; clean up UI either way
} catch (e) {
if (uiModule) uiModule.showError(`Could not delete document: ${e.message}`);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Double-delete (two tabs, or confirm twice quickly); deployments where DELETE requires credentials and this call's missing credentials option becomes a bug.
Related errors
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/a5372051d6b5b638.
Report an issue: GitHub.