odysseus-dev/odysseus · error · HTTPException

str(e)

Error message

str(e)

What it means

HTTP 500 from truncate_session's generic except: any non-KeyError exception while parsing the body or executing truncate_messages is logged as 'Truncate error {session_id}' and returned verbatim as str(e). The message content depends entirely on the underlying exception — inspect logs to identify whether it was JSON parsing, a bad keep_count type, or a session-manager internal failure.

Source

Thrown at routes/history/history_routes.py:259

            "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)
            return {"status": "ok"}
        except KeyError:
            raise HTTPException(404, "Session not found")

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the server log line 'Truncate error <session_id>:' for the root cause
  2. Ensure the request sends Content-Type: application/json with {"keep_count": <int>}
  3. Serialize concurrent truncate/message calls to one session client-side

Example fix

# before
requests.post(url, data='{keep_count: 2}')  # not JSON

# after
requests.post(url, json={"keep_count": 2})
Defensive patterns

Strategy: try-catch

Validate before calling

import json
def valid_truncate_body(body) -> bool:
    try:
        return isinstance(json.loads(json.dumps(body)), dict) \
            and isinstance(body.get('keep_count', 0), int)
    except (TypeError, ValueError):
        return False

Try / catch

try:
    resp = requests.post(f'{base}/api/session/{sid}/truncate', json={'keep_count': 2}, timeout=30)
    resp.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 500:
        log_server_pairing(f'Truncate error {sid}', e.response.text)  # correlate with server log
    raise

Prevention

When it happens

Trigger: Non-JSON request body (await request.json() raises), keep_count of an unexpected type that breaks truncate_messages, or an internal state mutation error in the session manager.

Common situations: Client sending text/plain bodies; proxies rewriting POST bodies; concurrency where two truncates race on the same session; keep_count sent as null.

Related errors


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