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

  1. Add credentials: 'same-origin' to the DELETE fetch to match every other call in the module.
  2. Check the status: 404 → treat as already-deleted and remove the tab anyway; 5xx → keep the tab and surface the error.
  3. 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

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.