odysseus-dev/odysseus · error · HTTPException
Failed to fetch document library: {e}
Error message
Failed to fetch document library: {e} What it means
Generic 500 wrapper around GET /api/documents/library: any unexpected exception while building the library listing (query construction, iteration over docs, or serialization such as .isoformat() on timestamps) is caught, logged as 'Failed to fetch document library: <e>', and re-raised as 500 after db.close().
Source
Thrown at routes/document/document_routes.py:440
"session_id": doc.session_id,
"session_name": session_name,
"title": doc.title,
"language": _library_language_for_document(doc),
"preview": (doc.current_content or "")[:500],
"version_count": doc.version_count,
"created_at": (doc.created_at.isoformat() + "Z") if doc.created_at else None,
"updated_at": (doc.updated_at.isoformat() + "Z") if doc.updated_at else None,
})
return {
"documents": documents,
"total": total,
"languages": languages,
"session_count": session_count,
}
except Exception as e:
logger.error(f"Failed to fetch document library: {e}")
raise HTTPException(500, f"Failed to fetch document library: {e}")
finally:
db.close()
# ---- GET /api/documents/{session_id} ----
@router.get("/api/documents/{session_id}")
async def list_documents(request: Request, session_id: str) -> List[Dict[str, Any]]:
user = get_current_user(request)
db = SessionLocal()
try:
if not user:
if not _auth_disabled():
raise HTTPException(403, "Authentication required")
# v2 review HIGH-9: raise 403 explicitly when the caller
# can't see this session, instead of returning [] which the
# UI treats identically to "no docs" and silently masks
# auth failures.
_get_session_or_404(db, session_id, user)
q = db.query(Document).filter(View on GitHub (pinned to f9235ebbf1)
Solutions
- Read the logged exception after 'Failed to fetch document library:' — it names the exact failing column or call.
- If it is AttributeError on None timestamps, backfill NULL created_at/updated_at values or guard them (the code already guards with if doc.created_at — check the other fields in the dict block).
- If it is OperationalError: database is locked, reduce concurrent writers or move to a proper RDBMS.
- Run the project's migration so all columns referenced by the serializer exist.
Example fix
# before "created_at": doc.created_at.isoformat() + "Z" # crashes on legacy NULL rows # after "created_at": (doc.created_at.isoformat() + "Z") if doc.created_at else None
Defensive patterns
Strategy: try-catch
Try / catch
try { const lib = await api.get('/api/documents/library'); }
catch (e) {
if (e.status === 500 && /document library/.test(e.message)) {
notify('Library temporarily unavailable'); // safe to retry after a delay
await delay(2000); return api.get('/api/documents/library');
}
throw e;
} Prevention
- Backfill NULL timestamp columns during migrations
- Guard every optional-field serialization with a None check
- Avoid concurrent heavy writers against SQLite
When it happens
Trigger: A row with corrupt/None created_at or updated_at hitting code that assumes a datetime; a DB connectivity failure mid-query; schema drift (selected column missing); an exception inside the per-doc dict building block shown above the handler.
Common situations: Older rows predating a migration that added timestamp columns (values NULL where code calls a method unconditionally); database locked by another writer in SQLite; upgrading the app without migrating data.
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/2d76a5947be3b832.
Report an issue: GitHub.