odysseus-dev/odysseus · error · Error

HTTP ${res.status}

Error message

HTTP ${res.status}

What it means

Thrown by libraryDeleteSingle in documentLibrary.js when DELETE /api/document/<docId> returns non-2xx. Unlike document.js's delete, this site parses the response body and prefers j.detail, falling back to `HTTP <status>`, and reports via 'Failed to delete document: <msg>'. The card is only removed after the transition, so failure leaves the card intact.

Source

Thrown at static/js/documentLibrary.js:1196

    if (archiveBtn) {
      archiveBtn.disabled = _librarySelectedIds.size === 0;
      archiveBtn.textContent = _libraryArchivedView ? 'Restore' : 'Archive';
    }
  }

  async function libraryDeleteSingle(docId, card) {
    if (uiModule && uiModule.styledConfirm) {
      const ok = await uiModule.styledConfirm('Delete this document?', { confirmText: 'Delete', danger: true });
      if (!ok) return;
    } else if (!confirm('Delete this document?')) {
      return;
    }
    try {
      const res = await fetch(`${API_BASE}/api/document/${docId}`, { method: 'DELETE', credentials: 'same-origin' });
      if (!res.ok) {
        let detail = `HTTP ${res.status}`;
        try { const j = await res.json(); if (j?.detail) detail = j.detail; } catch {}
        throw new Error(detail);
      }
      if (card) {
        card.classList.add('doclib-card-deleting');
        card.addEventListener('transitionend', () => card.remove(), { once: true });
        setTimeout(() => { if (card.parentElement) card.remove(); }, 400);
      }
      libraryRemoveDocumentFromState(docId);
      if (uiModule) uiModule.showToast('Document deleted');
    } catch (e) {
      if (uiModule) uiModule.showError(`Failed to delete document: ${e.message || e}`);
    }
  }

  async function libraryBulkDelete() {
    if (_librarySelectedIds.size === 0) return;
    const count = _librarySelectedIds.size;
    if (uiModule && uiModule.styledConfirm) {
      const ok = await uiModule.styledConfirm(

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the toast — it includes the backend detail, which usually names the cause.
  2. If 404, refresh the Library; the entry is stale and will disappear on reload (or treat 404 as success and remove the card locally).
  3. Re-authenticate if 401.
  4. Check server logs for 5xx during DELETE.

Example fix

// before
if (!res.ok) {
  let detail = `HTTP ${res.status}`;
  try { const j = await res.json(); if (j?.detail) detail = j.detail; } catch {}
  throw new Error(detail);
}

// after — treat 404 as already-deleted so the stale card still clears
if (!res.ok && res.status !== 404) {
  let detail = `HTTP ${res.status}`;
  try { const j = await res.json(); if (j?.detail || j?.error) detail = j.detail || j.error; } catch {}
  throw new Error(detail);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!docId || !/^[\w-]+$/.test(String(docId))) return;

Try / catch

try {
  const res = await fetch(`${API_BASE}/api/document/${docId}`, { method: 'DELETE', credentials: 'same-origin' });
  if (!res.ok && res.status !== 404) {
    let detail = `HTTP ${res.status}`;
    try { const j = await res.json(); if (j?.detail) detail = j.detail; } catch {}
    throw new Error(detail);
  }
  removeCard(card); // 404 means already deleted — still clear the stale card
} catch (a) {
  if (uiModule) uiModule.showError(`Failed to delete document: ${a.message || a}`);
}

Prevention

When it happens

Trigger: Deleting from the Library grid when the doc was already purged (404 with a FastAPI-style detail body), auth expired (401), or server 500. Unlike error 53 this call does send credentials: 'same-origin'.

Common situations: Deleting in one tab while the Library in another tab is stale; server-side cascade/constraint errors; session cookie expired between load and delete.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/57dbd9cfa70cffd2. Report an issue: GitHub.