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

  1. Send a non-empty message after trimming whitespace
  2. In n8n/Make, add an IF node that skips/branches when the message expression is empty
  3. 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

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


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