odysseus-dev/odysseus · error · HTTPException

Failed to retrieve logs: {str(e)}

Error message

Failed to retrieve logs: {str(e)}

What it means

Raised (HTTP 500) by the diagnostics logs endpoint (admin-gated) when anything inside the log-tail try block throws — most commonly an OSError while opening/reading the log file (missing file, permission denied, path is a directory) but the blanket `except Exception` also captures decode surprises. The original exception is logged as 'Diagnostics logs retrieval error' before the 500 is raised, with str(e) surfaced to the caller.

Source

Thrown at routes/diagnostics_routes.py:54

        try:
            log_file = os.path.join(DATA_DIR, "logs", "app.log")
            if not os.path.exists(log_file):
                return {"status": "success", "logs": []}

            # Safe tail read of the log file (max 5MB via rotation)
            with open(log_file, "r", encoding="utf-8", errors="ignore") as f:
                lines = f.readlines()

            tail_lines = lines[-limit:] if len(lines) > limit else lines
            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"}

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Check the server console for the logged 'Diagnostics logs retrieval error: ...' line — it contains the exact OSError reason.
  2. Verify the log file exists and is readable by the server process user (ls -l + sudo -u <user> head <file>).
  3. If the file was rotated/renamed, point the diagnostics config at the current log path or restart so logging recreates it.
  4. For huge logs, tail the file (seek from end) instead of relying on readlines() over the whole file.

Example fix

// before
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
    lines = f.readlines()

// after (streaming tail, no full-file read)
import subprocess
tail = subprocess.run(['tail', '-n', str(limit), log_file], capture_output=True, text=True)
tail_lines = tail.stdout.splitlines()
Defensive patterns

Strategy: try-catch

Validate before calling

# Pre-flight the log file is readable before requesting the tail
import os
if not os.path.isfile(log_file) or not os.access(log_file, os.R_OK):
    skip_diagnostics('log file missing or unreadable')

Try / catch

try { const r = await get('/api/logs?limit=200'); } catch (e) { if (e.status === 500 && /Failed to retrieve logs/.test(e.message)) { showBanner('Log file unavailable on server — see server console'); return []; } throw e; }

Prevention

When it happens

Trigger: GET of the logs tail endpoint when log_file points at a path that does not exist (logging not yet initialized / rotated away), is unreadable under the server's user, or when a filesystem error (NFS stall, disk issue) interrupts the read of a large log being read fully into memory via readlines().

Common situations: Fresh install where the log file has not been created yet; server runs under a different user than the one owning the logs directory; log rotated mid-request; extremely large log file causing memory pressure.

Related errors


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