{"record":{"id":"feb2512f78708d27","repo":"odysseus-dev/odysseus","slug":"session-session-not-found","errorCode":null,"errorMessage":"Session '{session}' not found","messagePattern":"Session '(.+?)' not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"routes/chat_routes.py","lineNumber":697,"sourceCode":"    async def chat_endpoint(request: Request, chat_request: ChatRequest) -> Dict[str, Any]:\n        _set_user_time_from_request(request)\n\n        message = chat_request.message\n        session = chat_request.session\n        att_ids = chat_request.attachments or []\n        use_web = chat_request.use_web\n        use_research = chat_request.use_research\n        time_filter = chat_request.time_filter\n        preset_id = chat_request.preset_id\n\n        # Verify the caller owns this session before loading it.\n        # Without this, any authenticated user can post into another user's chat.\n        _verify_session_owner(request, session)\n\n        try:\n            sess = session_manager.get_session(session)\n        except KeyError:\n            raise HTTPException(404, f\"Session '{session}' not found\")\n        owner = effective_user(request)\n        if _clear_orphaned_session_endpoint(sess, owner=owner):\n            raise HTTPException(400, \"Selected model endpoint was removed. Pick another model in Settings.\")\n\n        # Empty model + live endpoint = setup race (Issue #587). Repair from\n        # the endpoint's cached model list before privilege checks, which\n        # otherwise see \"\" and behave inconsistently with the allowlist.\n        _recover_empty_session_model(sess, session, owner=owner)\n        if not getattr(sess, \"model\", \"\").strip():\n            raise HTTPException(\n                400,\n                \"No model selected for this chat. Open the model picker and choose one before sending.\",\n            )\n        if not (getattr(sess, \"endpoint_url\", \"\") or \"\").strip():\n            raise HTTPException(400, \"Selected model endpoint is not configured\")\n\n        # Same allowed_models + daily-cap gate as chat_stream (mirror so the\n        # non-streaming path can't be used to bypass).","sourceCodeStart":679,"sourceCodeEnd":715,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_routes.py#L679-L715","documentation":"The non-streaming chat endpoint could not find the given session ID in the session manager after ownership verification passed. get_session raises KeyError for unknown IDs, which the handler maps to a 404. Ownership is checked first, so a foreign session also surfaces as this 404 rather than a 403.","triggerScenarios":"POST to the non-streaming chat route with a session ID that was deleted, never existed, or belongs to another user (cross-user IDs are masked as 404).","commonSituations":"Client kept a stale session ID after server restart cleared in-memory sessions (if not persisted); session deleted from another tab or by the sessions UI; hand-crafted session_id values.","solutions":["List sessions via the sessions API and use a current, owned session ID","Create a new session when the client gets this 404 instead of retrying the same ID","If sessions should survive restarts, verify session persistence/save is functioning"],"exampleFix":"// before\nfetch(`/api/chat/${sessionId}`, {method:'POST', body})\n// after\nconst r = await fetch(`/api/chat/${sessionId}`, {method:'POST', body});\nif (r.status === 404) { sessionId = await createNewSession(); /* retry once */ }","handlingStrategy":"fallback","validationCode":"const sessions = await fetch('/api/sessions').then(r=>r.json());\nconst ok = sessions.some(s => s.id === sessionId);\nif (!ok) sessionId = await createSession();","typeGuard":null,"tryCatchPattern":"try { await postChat(sessionId, body); }\ncatch (e) { if (e.status === 404 && /not found/.test(e.message)) { sessionId = await createSession(); await postChat(sessionId, body); } else throw e; }","preventionTips":["Reconcile the client's session list on app start","Treat session 404 as a reset signal: create fresh, drop the stale ID"],"tags":["session","not-found","fastapi","chat"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}