odysseus-dev/odysseus · error · Error
Document save failed: HTTP ${res.status}
Error message
Document save failed: HTTP ${res.status} What it means
Thrown by saveDocument in static/js/document.js when the document save request (PUT/POST to the document endpoint) returns non-2xx — but only after the 404 branch above it has already handled deleted documents (dropping the tab and showing 'Document no longer exists'). So this throw means the save genuinely failed with a non-404 error.
Source
Thrown at static/js/document.js:9439
}),
});
if (res.status === 404) {
if (silent && localDoc?.language === 'email') {
return;
}
// Streaming/empty email drafts can leave a local tab pointing at a temp
// or already-deleted document. Do not keep surfacing autosave errors for
// a document the backend no longer knows about.
if (docs.has(savingDocId)) docs.delete(savingDocId);
if (activeDocId === savingDocId) {
activeDocId = null;
renderTabs();
}
_syncDocIndicator();
if (!silent && uiModule) uiModule.showError('Document no longer exists');
return;
}
if (!res.ok) throw new Error(`Document save failed: HTTP ${res.status}`);
const doc = await res.json();
const badge = document.getElementById('doc-version-badge');
if (badge) { const _v = doc.version_count || 1; badge.textContent = `v${_v}`; badge.style.display = _v > 1 ? '' : 'none'; }
// Update map
if (docs.has(savingDocId)) {
docs.get(savingDocId).version = doc.version_count || 1;
docs.get(savingDocId).content = contentToSave;
}
_syncDocIndicator();
if (!silent && uiModule) uiModule.showToast(forceVersion ? 'New version saved' : 'Document saved');
} catch (e) {
console.error('Failed to save document:', e);
const now = Date.now();
if (uiModule && (!silent || now - _lastAutoSaveErrorAt > 10000)) {
uiModule.showError(silent ? 'Autosave failed' : 'Failed to save document');
_lastAutoSaveErrorAt = now;
}
}View on GitHub (pinned to f9235ebbf1)
Solutions
- Identify the status: 413 → raise payload limit or trim content; 401 → re-login; 409 → reload the doc and merge; 5xx → server logs.
- If autosaves spam this error, the debounce (800 ms) plus silent flag hides it — watch the Network tab during typing.
- For 404-adjacent ghost tabs the code already self-heals; for other statuses verify the doc still exists in the Library.
Example fix
// before
if (!res.ok) throw new Error(`Document save failed: HTTP ${res.status}`);
// after
if (!res.ok) {
let detail = '';
try { const j = await res.json(); detail = j?.detail || j?.error || ''; } catch (_) {}
throw new Error(`Document save failed: HTTP ${res.status}${detail ? ` — ${detail}` : ''}`);
} Defensive patterns
Strategy: retry
Validate before calling
const doc = docs.get(savingDocId); if (!doc) return; // nothing to save if (savingDocId == null || contentToSave == null) return;
Type guard
function isSavedDoc(doc) {
return doc != null && typeof doc === 'object'
&& doc.id != null
&& (doc.version_count == null || typeof doc.version_count === 'number');
} Try / catch
let lastErr = null;
for (let attempt = 0; attempt < 2; attempt++) {
try {
const res = await doSave();
if (res.status === 404) { dropGhostTab(savingDocId); return; }
if (!res.ok) throw new Error(`Document save failed: HTTP ${res.status}`);
const doc = await res.json();
if (!isSavedDoc(doc)) throw new Error('Invalid save response');
return;
} catch (e) { lastErr = e; }
}
if (!silent && uiModule) uiModule.showError(`Save failed: ${lastErr.message}`); Prevention
- Keep autosave debounced and silent-fail on transient errors, but surface persistent ones after N retries.
- Treat 409 conflicts explicitly: offer reload-and-merge instead of looping saves.
- Cap document size client-side to stay under request body limits.
When it happens
Trigger: Autosave or manual save hitting 400/422 (content too large, invalid payload), 401/403 (expired session), 409 (version conflict), or 500 (DB write failure). 404 never reaches here — it is intercepted earlier to clean up ghost tabs.
Common situations: Very large email drafts exceeding a body-size limit; server restart losing sessions; concurrent edits producing conflicts; DB locked during backup.
Related errors
- data?.error || data?.detail || `HTTP ${res.status}`
- (data && (data.error || data.detail)) || `HTTP ${res.status}
- Document create failed: HTTP ${res.status}
- HTTP ${res.status}
- res.statusText || String(res.status)
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/60a5243842d441e9.
Report an issue: GitHub.