odysseus-dev/odysseus · error · HTTPException

text is required

Error message

text is required

What it means

Thrown by a calendar utility endpoint that parses natural-language date expressions using an LLM. The handler reads the JSON body and requires a non-empty 'text' field after stripping whitespace. If 'text' is missing, null, or blank, the request is rejected with HTTP 400 before any model call is made.

Solutions

  1. Ensure the request body includes a non-empty 'text' field: {"text": "meeting next friday at 3pm", "tz": "America/New_York"}
  2. Trim and check the input client-side before sending; disable the submit action while the textarea is blank
  3. Verify the frontend field name matches 'text' exactly (not 'date_text', 'query', etc.)

Example fix

// before
fetch('/api/calendar/parse', {method:'POST', body: JSON.stringify({query: userInput})})
// after
if (!userInput.trim()) return;
fetch('/api/calendar/parse', {method:'POST', body: JSON.stringify({text: userInput.trim()})})
Defensive patterns

Strategy: validation

Validate before calling

const text = (userInput ?? '').trim();
if (!text) { showError('Enter a date expression to parse.'); return; }
await fetch(url, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({text})});

Type guard

function isValidParsePayload(b: unknown): b is {text: string} {
  return typeof b === 'object' && b !== null && typeof (b as any).text === 'string' && (b as any).text.trim().length > 0;
}

Prevention

When it happens

Trigger: POST to the calendar date-parsing route with a JSON body lacking 'text', with 'text': null, or with 'text': " " (whitespace only). The check is (body.get("text") or "").strip() so falsy or blank values all trigger it.

Common situations: Frontend sends an empty input box submit; a form field named differently (e.g. 'query' or 'date_text') than the API expects; a null from an optional UI state variable serialized directly into the request body.

Related errors


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

Appendix: source

Thrown at routes/calendar_routes.py:1641

        Input: {"text": "lunch with sara friday 1pm downtown", "tz": "America/New_York"}
        Output: {"ok": true, "event": {"summary", "dtstart", "dtend",
                  "all_day", "location", "description"}, "confidence": 0.0-1.0}

        Anchored on the server's current date/time so phrases like
        "tomorrow", "next Tuesday", "in 30 minutes" resolve correctly.
        Uses the "utility" endpoint (small / fast model) to keep latency low.
        """
        owner = _require_user(request)
        from src.endpoint_resolver import resolve_endpoint
        from src.llm_core import llm_call_async
        from src.text_helpers import strip_think
        import json as _json
        import re as _re

        body = await request.json()
        text = (body.get("text") or "").strip()
        if not text:
            raise HTTPException(400, "text is required")
        from src.user_time import (
            clear_user_time_context,
            current_datetime_prompt,
            now_user_local,
            set_user_tz_name,
            set_user_tz_offset,
        )

        clear_user_time_context()
        tz_hint = (body.get("tz") or "").strip()
        if body.get("tz_offset") is not None:
            set_user_tz_offset(body.get("tz_offset"))
        if tz_hint:
            set_user_tz_name(tz_hint)

        url, model, headers = resolve_endpoint("utility", owner=owner or None)
        if not url:
            url, model, headers = resolve_endpoint("default", owner=owner or None)

View on GitHub (pinned to f9235ebbf1)