{"record":{"id":"3ee11ad615ed0f45","repo":"odysseus-dev/odysseus","slug":"no-active-stream-for-this-session","errorCode":null,"errorMessage":"No active stream for this session","messagePattern":"No active stream for this session","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"info","filePath":"routes/chat_routes.py","lineNumber":2432,"sourceCode":"        stopped = agent_runs.stop(session_id, _expected_run_id)\n        return {\"stopped\": stopped}\n\n    # ------------------------------------------------------------------ #\n    # GET /api/chat/stream_status — check if a stream is active for a session\n    # ------------------------------------------------------------------ #\n    @router.get(\"/api/chat/stream_status/{session_id}\")\n    async def chat_stream_status(request: Request, session_id: str) -> Dict[str, Any]:\n        _verify_session_owner(request, session_id)\n        # A detached run can still be going even if _active_streams was popped;\n        # report it as active so the client knows to reconnect via /resume.\n        # Read once via .get() to avoid a KeyError race between the membership\n        # check and the indexed read if a sibling stream's finally pops the\n        # entry in between (same pattern _stream_set already uses).\n        rec = _active_streams.get(session_id)\n        if rec is None:\n            if agent_runs.is_active(session_id):\n                return {\"status\": \"streaming\", \"detached\": True}\n            raise HTTPException(404, \"No active stream for this session\")\n        return rec\n\n    # ------------------------------------------------------------------ #\n    # POST /api/inject_context\n    # ------------------------------------------------------------------ #\n    @router.post(\"/api/inject_context/{session_id}\")\n    async def inject_context(request: Request, session_id: str, context: str = Form(...)) -> Dict[str, str]:\n        _verify_session_owner(request, session_id)\n        try:\n            sess = session_manager.get_session(session_id)\n            msg = untrusted_context_message(\"injected research context\", f\"Research Context: {context}\")\n            sess.add_message(ChatMessage(msg[\"role\"], msg[\"content\"], metadata=msg.get(\"metadata\")))\n            session_manager.save_sessions()\n            return {\"status\": \"context_injected\"}\n        except KeyError:\n            raise HTTPException(404, \"Session not found\")\n\n    # ------------------------------------------------------------------ #","sourceCodeStart":2414,"sourceCodeEnd":2450,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_routes.py#L2414-L2450","documentation":"GET /api/chat/stream_status/{session_id} returns 404 when the session has neither an entry in _active_streams nor an active detached run in agent_runs. The endpoint is a presence probe: 'detached': true is reported when only the background run exists.","triggerScenarios":"Polling stream status for a session with no open SSE stream and no detached run — idle chats, finished runs whose stream record was popped by the finally block, or never-started sessions.","commonSituations":"Client status polling loop continuing after a stream ends; reconnect logic probing a chat that was closed; race where the stream record was just popped but no detached run replaced it.","solutions":["Treat this 404 as 'idle' in the client (no stream, no run) rather than as an error","Stop polling once 404 is returned repeatedly and the UI shows the chat as inactive","If a run should be active, verify it was started detached and the server wasn't restarted"],"exampleFix":"// before\nconst rec = await fetch(statusUrl).then(r => r.json());\n// after\nconst resp = await fetch(statusUrl);\nif (resp.status === 404) { /* session idle: render idle UI */ }\nconst rec = await resp.json();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"const resp = await fetch(statusUrl);\nif (resp.status === 404) { renderIdleState(); return; }\nconst rec = await resp.json();","preventionTips":["Model 404 from stream_status as 'idle', not an error","Back off or stop polling after repeated idle results"],"tags":["session","sse","status","not-found"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}