{"record":{"id":"401ae53389fd6fcf","repo":"odysseus-dev/odysseus","slug":"topic-analysis-failed-e","errorCode":null,"errorMessage":"Topic analysis failed: {e}","messagePattern":"Topic analysis failed: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/history/history_routes.py","lineNumber":658,"sourceCode":"                \"status\": \"ok\",\n                \"id\": new_id,\n                \"name\": fork_name,\n                \"kept\": len(msgs_to_copy),\n            }\n        except HTTPException:\n            raise\n        except Exception as e:\n            logger.error(f\"Fork error {session_id}: {e}\")\n            raise HTTPException(500, str(e))\n\n    @router.get(\"/api/conversations/topics\")\n    async def get_conversation_topics(request: Request) -> Dict[str, Any]:\n        from src.auth_helpers import require_user\n        user = require_user(request)\n        try:\n            return analyze_topics(session_manager, owner=user or None)\n        except Exception as e:\n            raise HTTPException(500, f\"Topic analysis failed: {e}\")\n\n    @router.get(\"/api/session/{session_id}/context\")\n    async def get_session_context_usage(request: Request, session_id: str) -> Dict[str, Any]:\n        \"\"\"Return an estimated whole-chat context usage for the session's model.\n\n        Streaming footers report the prompt size for the last request. This\n        endpoint estimates the persisted session context so the header can show\n        when the whole chat is approaching compaction.\n        \"\"\"\n        _verify_session_owner(request, session_id)\n        try:\n            session = session_manager.get_session(session_id)\n        except KeyError:\n            raise HTTPException(404, \"Session not found\")\n\n        try:\n            from src.model_context import estimate_tokens, get_context_length\n","sourceCodeStart":640,"sourceCodeEnd":676,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/history/history_routes.py#L640-L676","documentation":"GET /api/conversations/topics wraps analyze_topics(session_manager, owner) in a blanket except that returns 500 'Topic analysis failed: {e}'. Any failure inside topic analysis — empty/malformed histories, text-processing exceptions, DB reads during iteration — surfaces here with the raw exception interpolated into the message.","triggerScenarios":"Opening the topics view when analyze_topics hits a message with unexpected content types, a session whose history contains None entries, a DB read error mid-iteration, or an owner filter that yields inconsistent cache state.","commonSituations":"Topics endpoint hit right after a restart with partially hydrated sessions; a conversation containing non-text content that the analyzer cannot process; auth disabled/enabled toggle changing the owner value passed in.","solutions":["Read the server log / response body — '{e}' names the exact exception raised inside analyze_topics (src/topic_analyzer.py:21)","Retry after all sessions finish hydrating; freshly restarted servers may expose transient half-loaded histories","If one conversation triggers it, inspect that session's history for None/malformed messages","Treat the topics view as best-effort: it is analytic, not transactional, so the app remains usable without it"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { return await getTopics(); }\ncatch (e) { if (e.status === 500) return { topics: [] }; } // topics are best-effort analytics","preventionTips":["Treat the topics view as optional — degrade to an empty state on failure","Retry after sessions finish hydrating post-restart","Report persistent failures with the '{e}' text from the response"],"tags":["fastapi","http-500","topic-analysis","error-handling"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}