odysseus-dev/odysseus · error · HTTPException

Failed to retrieve database statistics

Error message

Failed to retrieve database statistics

What it means

Raised (HTTP 500) by GET /api/db/stats (admin-gated) when importing core.database.get_detailed_stats or executing it throws. Unlike the logs endpoint, the raw exception text is NOT returned — only the generic message — but the cause is logged as 'DB stats error'. Typical causes: SQLite database file missing/locked/corrupted, schema mismatch after an upgrade, or an import failure inside core.database.

Source

Thrown at routes/diagnostics_routes.py:64

            tail_lines = [line.rstrip('\r\n') for line in tail_lines]

            return {
                "status": "success",
                "logs": tail_lines
            }
        except Exception as e:
            logger.error(f"Diagnostics logs retrieval error: {e}")
            raise HTTPException(500, f"Failed to retrieve logs: {str(e)}")

    @router.get("/api/db/stats")
    async def get_database_stats(request: Request) -> Dict[str, Any]:
        require_admin(request)
        try:
            from core.database import get_detailed_stats
            return get_detailed_stats()
        except Exception as e:
            logger.error(f"DB stats error: {e}")
            raise HTTPException(500, "Failed to retrieve database statistics")

    @router.get("/api/rag/stats")
    async def get_rag_stats(request: Request) -> Dict[str, Any]:
        require_admin(request)
        if rag_available and rag_manager:
            return rag_manager.get_stats()
        return {"error": "RAG system not available"}

    @router.get("/api/test/youtube")
    async def test_youtube(request: Request, url: str) -> Dict[str, Any]:
        require_admin(request)
        try:
            video_id = extract_youtube_id(url)
            if not video_id:
                return {"error": "Invalid YouTube URL"}

            data = await extract_transcript_async(url, video_id)
            return {

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the server log for the 'DB stats error: ...' line to get the real exception.
  2. If it is 'database is locked', stop competing processes (second server instance, manual sqlite3 session) or enable WAL mode.
  3. If it is 'no such table/column', run the app's DB init/migration path (init_db) so the schema is brought up to date.
  4. If the file was moved/deleted, restore it or let the app recreate it, then restart.
Defensive patterns

Strategy: try-catch

Validate before calling

# Pre-flight: DB reachable and schema present before the dashboard calls stats
from core.database import SessionLocal
with SessionLocal() as db:
    db.execute(text('SELECT 1'))

Try / catch

try { const stats = await get('/api/db/stats'); } catch (e) { if (e.status === 500 && /database statistics/.test(e.message)) { showBanner('DB stats unavailable — check server log for DB stats error'); return null; } throw e; }

Prevention

When it happens

Trigger: GET /api/db/stats while the database file is locked by another writer (SQLite BUSY), the DB was deleted or moved after startup, a migration left the schema without a table/column get_detailed_stats queries, or core.database import fails (missing optional dependency).

Common situations: Two server instances pointed at the same SQLite file; DB directory on a network mount with flaky locking; app upgraded to a version whose get_detailed_stats expects a column the old DB lacks; database file permissions changed.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/de90ddda7bd28db2. Report an issue: GitHub.