odysseus-dev/odysseus · error · HTTPException
Session not found
Error message
Session not found
What it means
HTTP 404 from POST /v1/chat when resuming a session: session_manager.get_session(session_id) raised (KeyError or any Exception). The broad except (KeyError, Exception) intentionally converts every lookup failure — unknown id, expired/purged session, corrupted session store — into the same 404 so it does not leak internals.
Source
Thrown at routes/webhook/webhook_routes.py:262
token_owner = getattr(request.state, "api_token_owner", None)
from core.models import ChatMessage
from src.llm_core import llm_call_async
from src.endpoint_resolver import build_chat_url, build_headers, build_models_url, normalize_base
message = body.message.strip()
if not message:
raise HTTPException(400, "Message is required")
session_id = body.session
sess = None
# --- Case 1: Resume an existing session ---
if session_id and session_manager:
try:
sess = session_manager.get_session(session_id)
except (KeyError, Exception):
raise HTTPException(404, "Session not found")
# SECURITY: verify the API-token's user owns this session — without
# this any token holder could resume any user's chat by passing its
# ID. The token's user is on request.state.user (set by API-token
# middleware); fall back to require_user if not present.
try:
from src.auth_helpers import get_current_user as _gcu
_tok_user = token_owner or getattr(request.state, "user", None) or _gcu(request)
except Exception:
_tok_user = None
# Strict ownership (see _caller_owns_session): fail closed so a
# null-owner / cross-owner session can't be resumed by an arbitrary
# chat-scoped token.
_sess_owner = getattr(sess, "owner", None)
if not _caller_owns_session(_sess_owner, _tok_user):
raise HTTPException(404, "Session not found")
# --- Case 2: Direct API key + model (no pre-configured endpoint needed) ---
if not sess and body.api_key:View on GitHub (pinned to f9235ebbf1)
Solutions
- Start a fresh conversation by omitting body.session, capture the new session id from the response, and store that
- Verify the session exists (GET session endpoint / admin UI) before reusing it
- Ensure session persistence is enabled so ids survive restarts
Defensive patterns
Strategy: fallback
Try / catch
if resp.status_code == 404 and 'Session not found' in detail:
resp = chat(message=body.message, api_key=..., model=...) # fresh session, no `session` field
store_new_session_id(resp["session"]) Prevention
- Store the session id returned by each chat call and use only the latest
- Expect sessions to disappear after restarts; design automation to restart conversations gracefully
When it happens
Trigger: Passing a session id that never existed, was deleted, or was lost when the process restarted and sessions were not persisted/loaded; a session id from a different deployment.
Common situations: Automation retrying an old conversation after server restart without saved sessions; long-lived n8n workflows storing session ids past their retention; typo'd/truncated UUID.
Related errors
- Session '{session}' not found
- str(e)
- Session not found
- Session {session_id} not found
- Integration not found
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/09c58fd13c9f7d24.
Report an issue: GitHub.