{"record":{"id":"2d76a5947be3b832","repo":"odysseus-dev/odysseus","slug":"failed-to-fetch-document-library-e","errorCode":null,"errorMessage":"Failed to fetch document library: {e}","messagePattern":"Failed to fetch document library: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/document/document_routes.py","lineNumber":440,"sourceCode":"                    \"session_id\": doc.session_id,\n                    \"session_name\": session_name,\n                    \"title\": doc.title,\n                    \"language\": _library_language_for_document(doc),\n                    \"preview\": (doc.current_content or \"\")[:500],\n                    \"version_count\": doc.version_count,\n                    \"created_at\": (doc.created_at.isoformat() + \"Z\") if doc.created_at else None,\n                    \"updated_at\": (doc.updated_at.isoformat() + \"Z\") if doc.updated_at else None,\n                })\n\n            return {\n                \"documents\": documents,\n                \"total\": total,\n                \"languages\": languages,\n                \"session_count\": session_count,\n            }\n        except Exception as e:\n            logger.error(f\"Failed to fetch document library: {e}\")\n            raise HTTPException(500, f\"Failed to fetch document library: {e}\")\n        finally:\n            db.close()\n\n    # ---- GET /api/documents/{session_id} ----\n    @router.get(\"/api/documents/{session_id}\")\n    async def list_documents(request: Request, session_id: str) -> List[Dict[str, Any]]:\n        user = get_current_user(request)\n        db = SessionLocal()\n        try:\n            if not user:\n                if not _auth_disabled():\n                    raise HTTPException(403, \"Authentication required\")\n            # v2 review HIGH-9: raise 403 explicitly when the caller\n            # can't see this session, instead of returning [] which the\n            # UI treats identically to \"no docs\" and silently masks\n            # auth failures.\n            _get_session_or_404(db, session_id, user)\n            q = db.query(Document).filter(","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/document/document_routes.py#L422-L458","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\n\"created_at\": doc.created_at.isoformat() + \"Z\"  # crashes on legacy NULL rows\n# after\n\"created_at\": (doc.created_at.isoformat() + \"Z\") if doc.created_at else None","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { const lib = await api.get('/api/documents/library'); }\ncatch (e) {\n  if (e.status === 500 && /document library/.test(e.message)) {\n    notify('Library temporarily unavailable'); // safe to retry after a delay\n    await delay(2000); return api.get('/api/documents/library');\n  }\n  throw e;\n}","preventionTips":["Backfill NULL timestamp columns during migrations","Guard every optional-field serialization with a None check","Avoid concurrent heavy writers against SQLite"],"tags":["fastapi","sqlalchemy","serialization","http-500","database"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}