odysseus-dev/odysseus · error · HTTPException
Failed to update document: {e}
Error message
Failed to update document: {e} What it means
Generic 500 wrapper on PUT /api/document/{doc_id}: any unexpected exception during the update flow (email-content coercion, version coalescing, DocumentVersion insert, commit) is rolled back and re-raised as 'Failed to update document: <e>'. Real HTTPExceptions (e.g. the 404 or ownership 403) pass through untouched.
Source
Thrown at routes/document/document_routes.py:693
id=str(uuid.uuid4()),
document_id=doc_id,
version_number=new_ver,
content=incoming_content,
summary=req.summary or "Manual edit",
source="user",
)
doc.version_count = new_ver
db.add(ver)
doc.current_content = incoming_content
db.commit()
db.refresh(doc)
return _doc_to_dict(doc)
except HTTPException:
raise
except Exception as e:
db.rollback()
raise HTTPException(500, f"Failed to update document: {e}")
finally:
db.close()
# ---- PATCH /api/document/{doc_id} — metadata only ----
@router.patch("/api/document/{doc_id}")
async def patch_document(request: Request, doc_id: str, req: DocumentPatch) -> Dict[str, Any]:
user = get_current_user(request)
db = SessionLocal()
try:
doc = db.query(Document).filter(Document.id == doc_id).first()
if not doc:
raise HTTPException(404, "Document not found")
_verify_doc_owner(db, doc, user)
if req.title is not None:
doc.title = req.title
if req.language is not None:
doc.language = req.language
if req.session_id is not None:View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the interpolated exception and the server traceback for the failing stage.
- For lock timeouts, serialize writes per document or move off SQLite for concurrent use.
- Run migrations so DocumentVersion/Document columns match the models.
- Reproduce with the exact content payload if the email-coercion path is implicated.
Example fix
# before
await api.put(`/api/document/${id}`, { content }); // 500 Failed to update document: database is locked
# after
# single-writer queue per doc id, e.g. in the frontend:
await saveQueue.enqueue(id, () => api.put(`/api/document/${id}`, { content })); Defensive patterns
Strategy: retry
Try / catch
try { await api.put(`/api/document/${id}`, { content }); }
catch (e) {
if (e.status === 500 && /Failed to update document/.test(e.message)) {
if (/locked|timeout/i.test(e.message)) return retryWithBackoff(() => api.put(`/api/document/${id}`, { content }), 3);
notify('Save failed: ' + e.message);
} else throw e;
} Prevention
- Serialize saves per document (single autosave queue)
- Retry transient lock/timeout failures with backoff
- Keep local drafts so a failed save never loses user input
When it happens
Trigger: DB failure during commit (constraint, lock, connection loss); exception in _coerce_email_document_content on unusual content; version-row insert violating a constraint; schema drift in DocumentVersion columns.
Common situations: Concurrent edits from two tabs causing lock errors on SQLite; unmigrated DocumentVersion table; malformed content payloads hitting an unguarded branch in the email coercion helper.
Related errors
- Failed to delete calendar
- Failed to list calendars
- Failed to create calendar
- Failed to update calendar
- Failed to create document: {e}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/2c5c3c623f4fda1b.
Report an issue: GitHub.