{"record":{"id":"0f6662cb09678cb8","repo":"odysseus-dev/odysseus","slug":"str-e-0f6662","errorCode":null,"errorMessage":"str(e)","messagePattern":"str\\(e\\)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/history/history_routes.py","lineNumber":259,"sourceCode":"            \"history\": history_dict,\n            \"model\": session.model,\n            \"endpoint_url\": session.endpoint_url,\n            \"name\": session.name,\n        }\n\n    @router.post(\"/api/session/{session_id}/truncate\")\n    async def truncate_session(request: Request, session_id: str):\n        _verify_session_owner(request, session_id)\n        try:\n            body = await request.json()\n            keep_count = body.get(\"keep_count\", 0)\n            result = session_manager.truncate_messages(session_id, keep_count)\n            return {\"status\": \"ok\", \"kept\": keep_count, \"truncated\": result}\n        except KeyError:\n            raise HTTPException(404, \"Session not found\")\n        except Exception as e:\n            logger.error(f\"Truncate error {session_id}: {e}\")\n            raise HTTPException(500, str(e))\n\n    @router.post(\"/api/session/{session_id}/message\")\n    async def add_message(request: Request, session_id: str):\n        \"\"\"Add a message to a session (for slash command persistence).\"\"\"\n        _verify_session_owner(request, session_id)\n        try:\n            body = await request.json()\n            role = body.get(\"role\", \"assistant\")\n            content = body.get(\"content\", \"\")\n            if not content:\n                raise HTTPException(400, \"content is required\")\n            metadata = body.get(\"metadata\")\n            _reserve_message_uploads(request, content, metadata)\n            msg = ChatMessage(role=role, content=content, metadata=metadata)\n            session_manager.add_message(session_id, msg)\n            return {\"status\": \"ok\"}\n        except KeyError:\n            raise HTTPException(404, \"Session not found\")","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/history/history_routes.py#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"Client sending text/plain bodies; proxies rewriting POST bodies; concurrency where two truncates race on the same session; keep_count sent as null.","solutions":["Read the server log line 'Truncate error <session_id>:' for the root cause","Ensure the request sends Content-Type: application/json with {\"keep_count\": <int>}","Serialize concurrent truncate/message calls to one session client-side"],"exampleFix":"# before\nrequests.post(url, data='{keep_count: 2}')  # not JSON\n\n# after\nrequests.post(url, json={\"keep_count\": 2})","handlingStrategy":"try-catch","validationCode":"import json\ndef valid_truncate_body(body) -> bool:\n    try:\n        return isinstance(json.loads(json.dumps(body)), dict) \\\n            and isinstance(body.get('keep_count', 0), int)\n    except (TypeError, ValueError):\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    resp = requests.post(f'{base}/api/session/{sid}/truncate', json={'keep_count': 2}, timeout=30)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if e.response.status_code == 500:\n        log_server_pairing(f'Truncate error {sid}', e.response.text)  # correlate with server log\n    raise","preventionTips":["Always send Content-Type: application/json (use the json= parameter of your HTTP client)","Send keep_count as an integer, never null or a string","Serialize session mutations so concurrent truncate/message calls don't race"],"tags":["http-500","session","truncate","catch-all","request-body"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}