odysseus-dev/odysseus · warning · HTTPException

Session not found

Error message

Session not found

What it means

HTTP 404 from POST /api/session/{session_id}/truncate: session_manager.truncate_messages raised KeyError because the session id is not registered in the in-memory session manager. The route had already passed _verify_session_owner, so this is purely 'session absent from session_manager', most commonly after a server restart or for a DB-only session.

Source

Thrown at routes/history/history_routes.py:256

                db.close()

        return {
            "history": history_dict,
            "model": session.model,
            "endpoint_url": session.endpoint_url,
            "name": session.name,
        }

    @router.post("/api/session/{session_id}/truncate")
    async def truncate_session(request: Request, session_id: str):
        _verify_session_owner(request, session_id)
        try:
            body = await request.json()
            keep_count = body.get("keep_count", 0)
            result = session_manager.truncate_messages(session_id, keep_count)
            return {"status": "ok", "kept": keep_count, "truncated": result}
        except KeyError:
            raise HTTPException(404, "Session not found")
        except Exception as e:
            logger.error(f"Truncate error {session_id}: {e}")
            raise HTTPException(500, str(e))

    @router.post("/api/session/{session_id}/message")
    async def add_message(request: Request, session_id: str):
        """Add a message to a session (for slash command persistence)."""
        _verify_session_owner(request, session_id)
        try:
            body = await request.json()
            role = body.get("role", "assistant")
            content = body.get("content", "")
            if not content:
                raise HTTPException(400, "content is required")
            metadata = body.get("metadata")
            _reserve_message_uploads(request, content, metadata)
            msg = ChatMessage(role=role, content=content, metadata=metadata)
            session_manager.add_message(session_id, msg)

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Refresh the session list and confirm the id is live before truncating
  2. Reopen/load the session so session_manager registers it, then truncate
  3. Handle 404 by silently dropping the stale tab's truncation request
Defensive patterns

Strategy: fallback

Validate before calling

def can_truncate(session_id: str) -> bool:
    r = requests.get(f'{base}/api/sessions', headers=hdrs, timeout=30)
    return any(s.get('id') == session_id for s in r.json().get('sessions', r.json()))

Try / catch

try:
    truncate_session(sid, keep_count=2)
except HTTPError as e:
    if e.response.status_code == 404:
        pass  # session gone (server restart) — nothing to truncate, converge UI
    else:
        raise

Prevention

When it happens

Trigger: POST {"keep_count": 2} to /api/session/{id}/truncate for an unknown/restarted-away session; truncating a session that exists in the DB but was never loaded into memory.

Common situations: Client resume flow after server restart; long-lived browser tab with a stale session id; splitting truncation between DB-backed and memory-backed sessions.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/50adf0967a5c0d75. Report an issue: GitHub.