{"record":{"id":"9ece76679be2dce2","repo":"odysseus-dev/odysseus","slug":"str-e-9ece76","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"error","filePath":"routes/chat_routes.py","lineNumber":1103,"sourceCode":"                )\n            if isinstance(message, str) and _is_contextual_browser_followup(message, sess):\n                _explicit_browser_intent = True\n                if chat_mode == \"chat\":\n                    chat_mode = \"agent\"\n                    auto_escalated = True\n                    _workspace_agent_intent = False\n                    logger.info(\"chat→agent auto-escalation: contextual browser/form follow-up\")\n            if not workspace and isinstance(message, str):\n                _auto_workspace, _ = _resolve_workspace_from_message_path(request, message)\n                if _auto_workspace:\n                    workspace = _auto_workspace\n                    chat_mode = \"agent\"\n                    auto_escalated = True\n                    _workspace_agent_intent = True\n                    allow_bash = \"true\"\n                    logger.info(\"chat→agent auto-escalation: explicit path workspace=%s\", workspace)\n        except SessionNotFoundError as e:\n            raise HTTPException(404, str(e))\n        except (ValueError, ValidationError):\n            raise HTTPException(400, \"Invalid request parameters\")\n\n        # ------------------------------------------------------------------ #\n        # Privilege gates that must fire BEFORE any LLM work / token spend.\n        #   1. allowed_models — reject if session.model isn't in the user's\n        #      configured allowlist (empty list = \"no restriction\").\n        #   2. max_messages_per_day — count user-role ChatMessage rows owned\n        #      by this user in the last UTC day; 429 if at/over the cap.\n        # Admins always have full privileges via get_privileges (returns\n        # ADMIN_PRIVILEGES wholesale) so this is a no-op for them.\n        _enforce_chat_privileges(request, sess)\n\n        # Ensure session has auth headers\n        resolve_session_auth(sess, session, owner=effective_user(request))\n\n        # Check for research_pending BEFORE mode persist overwrites it\n        do_research = str(use_research).lower() == \"true\"","sourceCodeStart":1085,"sourceCodeEnd":1121,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/chat_routes.py#L1085-L1121","documentation":"During the streaming send's request-shaping phase, session resolution raised SessionNotFoundError (distinct from a plain KeyError) and the handler surfaces its message verbatim as a 404. This typically comes from coerce_message_and_session or later session loads when the requested session does not exist.","triggerScenarios":"POST /api/chat_stream with a session ID that is absent from the session manager at the coercion/loading step — deleted sessions, stale IDs after restart, or IDs the caller does not own.","commonSituations":"Client cached an old session ID; session deleted in another tab; server restarted with in-memory session state; concurrent delete-while-sending races.","solutions":["Fetch the current session list and use a valid ID, or create a new session","Handle 404 on stream open by resetting the client's session state instead of retrying","Check server logs for concurrent deletion if the ID was recently valid"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"const exists = await fetch(`/api/sessions/${sessionId}/exists`).then(r=>r.ok);\nif (!exists) sessionId = await createSession();","typeGuard":null,"tryCatchPattern":"try { await streamChat(sessionId, ...); }\ncatch (e) { if (e.status === 404) { sessionId = await createSession(); await streamChat(sessionId, ...); } else throw e; }","preventionTips":["Refresh session IDs on reconnect/restart","Treat 404 mid-conversation as 'start a new session', never a blind retry"],"tags":["session","not-found","chat-stream","http-404"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}