odysseus-dev/odysseus · warning · HTTPException
content is required
Error message
content is required
What it means
HTTP 400 from POST /api/session/{session_id}/message: the body's 'content' field must be a non-empty string after defaulting to ''. Empty message text is rejected before upload-reference reservation or ChatMessage creation. role defaults to 'assistant' and is not validated here.
Source
Thrown at routes/history/history_routes.py:270
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")
@router.post("/api/session/{session_id}/delete-messages")
async def delete_messages(request: Request, session_id: str):
"""Delete specific messages by DB ID (or legacy index)."""
_verify_session_owner(request, session_id)
try:
body = await request.json()
msg_ids = body.get("msg_ids", [])
indices = body.get("indices") # legacy fallback
session = session_manager.get_session(session_id)View on GitHub (pinned to f9235ebbf1)
Solutions
- Include non-empty content: {"role": "user", "content": "hello"}
- Skip the API call entirely when the message body is empty (guard client-side)
- If persisting slash-command output, check the result is non-empty before posting
Example fix
# before
if True: post(f'/api/session/{sid}/message', json={'role':'user','content': text})
# after
if text and text.strip():
post(f'/api/session/{sid}/message', json={'role': 'user', 'content': text}) Defensive patterns
Strategy: validation
Validate before calling
def valid_message_body(body: dict) -> bool:
content = body.get('content', '')
return isinstance(content, str) and bool(content.strip()) Type guard
function isValidMessage(b: unknown): b is { role: string; content: string } {
return typeof (b as any)?.content === 'string' && (b as any).content.trim().length > 0;
} Prevention
- Skip the API call for empty/whitespace content — it can never succeed
- Guard slash-command persistence to fire only when command output is non-empty
- Trim content client-side and disable send on empty input
When it happens
Trigger: POST with {"role": "user"} (no content), {"content": ""}, or {"content": null}; slash-command persistence layer firing with an empty command result.
Common situations: UI allowing send of whitespace-only or aborted messages; slash command producing empty output being persisted anyway; script echoing empty strings for assistant turns.
Related errors
- No documents specified
- Provide at least one point, box, or object text
- Box must be [x1, y1, x2, y2]
- markdown is required
- query is required
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/ec208c93f23c59cf.
Report an issue: GitHub.