odysseus-dev/odysseus · error · HTTPException
Message is required
Error message
Message is required
What it means
HTTP 400 from POST /v1/chat: body.message is empty after strip(). The Pydantic field requires the message key, but a whitespace-only string passes the schema and is only caught by this explicit check.
Source
Thrown at routes/webhook/webhook_routes.py:252
base_url: Optional[str] = Field(None, max_length=MAX_URL_LEN)
provider: Optional[str] = Field(None, max_length=50)
@router.post("/v1/chat")
async def sync_chat(request: Request, body: SyncChatRequest):
if not getattr(request.state, "api_token", False):
raise HTTPException(403, "This endpoint requires an API token")
scopes = set(getattr(request.state, "api_token_scopes", []) or [])
if "chat" not in scopes:
raise HTTPException(403, "API token is not scoped for chat")
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:View on GitHub (pinned to f9235ebbf1)
Solutions
- Send a non-empty message after trimming whitespace
- In n8n/Make, add an IF node that skips/branches when the message expression is empty
- Validate on the client before POSTing
Defensive patterns
Strategy: validation
Validate before calling
msg = (body.get("message") or "").strip()
if not msg:
skip_or_branch() # do not POST Type guard
def is_sendable_message(v: unknown) -> bool:
return isinstance(v, str) and len(v.strip()) > 0 Prevention
- Trim and check the message in the automation node before the HTTP call
- Add an IF/branch on empty expressions in n8n/Make templates
When it happens
Trigger: Posting {"message": ""} or {"message": " \n"}; an automation template whose message variable rendered empty (e.g. n8n expression resolved to blank).
Common situations: Workflow templates with unfilled placeholders; upstream LLM/step producing an empty string that gets forwarded; JSON forms without required-field validation.
Related errors
- {key} must be an integer
- Password is required
- Document is not linked to a source PDF
- msg_id and content are required
- detail
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/2788e6daa9ad99e6.
Report an issue: GitHub.