odysseus-dev/odysseus · error · Error
res.statusText || String(res.status)
Error message
res.statusText || String(res.status)
What it means
Error thrown during undo-save of a PDF-linked document: PUT /api/document/{docId} with the previous content returned non-OK. Only statusText or the numeric status is used — the body is ignored, so the reason is usually vague (and many proxies send empty statusText). The UI then sets the save pill to 'error' with the undo-failed message.
Source
Thrown at static/js/document.js:1061
if (!prev) return false;
_pdfUndoStackByDoc.set(docId, stack);
if (_pdfPaneSaveTimer) {
clearTimeout(_pdfPaneSaveTimer);
_pdfPaneSaveTimer = null;
}
const doc = docs.get(docId);
if (!doc) return false;
doc.content = prev;
const ta = document.getElementById('doc-editor-textarea');
if (ta) ta.value = prev;
_setPdfSaveStatus('saving');
try {
const res = await fetch(`${API_BASE}/api/document/${docId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ content: prev }),
});
if (!res.ok) throw new Error(res.statusText || String(res.status));
_setPdfSaveStatus('saved');
_renderPdfPane();
return true;
} catch (e) {
_setPdfSaveStatus('error', e.message || 'Undo failed');
return true;
}
}
// Active drop mode for the PDF toolbar — toolbar buttons set this; the
// next click on a page consumes it. null means clicks do nothing.
let _pdfDropMode = null;
// Per-doc last-used line spacing for text annotations. Once the user picks
// 1.6 for one box, every text box dropped after that defaults to 1.6.
const _pdfLastLineHeight = new Map(); // docId -> number
function _setPdfDropMode(mode) {
_pdfDropMode = mode;
const pane = document.getElementById('doc-pdf-view');View on GitHub (pinned to f9235ebbf1)
Solutions
- curl -i -X PUT the same JSON to see the status and body the code drops
- Reload the session/document list to resync docId state if 404/409
- Free server disk / check write permissions for 500s
- Raise proxy client_max_body_size if large content triggers 413
Example fix
// before
if (!res.ok) throw new Error(res.statusText || String(res.status));
// after
if (!res.ok) {
const t = await res.text().catch(() => '');
throw new Error(t || res.statusText || `HTTP ${res.status}`);
} Defensive patterns
Strategy: fallback
Validate before calling
if (!docs.has(docId)) return false; // undo target already gone
Try / catch
try {
const res = await fetch(`${API_BASE}/api/document/${docId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ content: prev }) });
if (!res.ok) {
const t = await res.text().catch(() => '');
throw new Error(t || res.statusText || `HTTP ${res.status}`);
}
_setPdfSaveStatus('saved');
_renderPdfPane();
return true;
} catch (e) {
_setPdfSaveStatus('error', e.message || 'Undo failed');
return true; // keep local content rolled back even if persist failed
} Prevention
- Include the response body — statusText is often empty on modern proxies
- Keep the local rollback (already done) so the editor state stays consistent on save failure
- Handle 404/409 by offering to reload the doc rather than retrying the same PUT
When it happens
Trigger: PUT /api/document/{id} returns 404 (doc deleted elsewhere), 409 (optimistic-concurrency conflict), 413 (content grew beyond a body limit), 500 (storage write failure).
Common situations: Document deleted in another tab/session while this one held an old copy; disk full on the server; proxy body-size limit exceeded after pasting large content into the editor; empty statusText making the message just '500'.
Related errors
- t || r2.statusText
- t || r.statusText
- err || res.statusText
- await _pdfResponseErrorMessage(res)
- t || res.statusText
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/7c911f486309b0bd.
Report an issue: GitHub.