{"record":{"id":"e945f54e12373c19","repo":"odysseus-dev/odysseus","slug":"text-is-required","errorCode":null,"errorMessage":"text is required","messagePattern":"text is required","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/calendar_routes.py","lineNumber":1641,"sourceCode":"        Input: {\"text\": \"lunch with sara friday 1pm downtown\", \"tz\": \"America/New_York\"}\n        Output: {\"ok\": true, \"event\": {\"summary\", \"dtstart\", \"dtend\",\n                  \"all_day\", \"location\", \"description\"}, \"confidence\": 0.0-1.0}\n\n        Anchored on the server's current date/time so phrases like\n        \"tomorrow\", \"next Tuesday\", \"in 30 minutes\" resolve correctly.\n        Uses the \"utility\" endpoint (small / fast model) to keep latency low.\n        \"\"\"\n        owner = _require_user(request)\n        from src.endpoint_resolver import resolve_endpoint\n        from src.llm_core import llm_call_async\n        from src.text_helpers import strip_think\n        import json as _json\n        import re as _re\n\n        body = await request.json()\n        text = (body.get(\"text\") or \"\").strip()\n        if not text:\n            raise HTTPException(400, \"text is required\")\n        from src.user_time import (\n            clear_user_time_context,\n            current_datetime_prompt,\n            now_user_local,\n            set_user_tz_name,\n            set_user_tz_offset,\n        )\n\n        clear_user_time_context()\n        tz_hint = (body.get(\"tz\") or \"\").strip()\n        if body.get(\"tz_offset\") is not None:\n            set_user_tz_offset(body.get(\"tz_offset\"))\n        if tz_hint:\n            set_user_tz_name(tz_hint)\n\n        url, model, headers = resolve_endpoint(\"utility\", owner=owner or None)\n        if not url:\n            url, model, headers = resolve_endpoint(\"default\", owner=owner or None)","sourceCodeStart":1623,"sourceCodeEnd":1659,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1623-L1659","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure the request body includes a non-empty 'text' field: {\"text\": \"meeting next friday at 3pm\", \"tz\": \"America/New_York\"}","Trim and check the input client-side before sending; disable the submit action while the textarea is blank","Verify the frontend field name matches 'text' exactly (not 'date_text', 'query', etc.)"],"exampleFix":"// before\nfetch('/api/calendar/parse', {method:'POST', body: JSON.stringify({query: userInput})})\n// after\nif (!userInput.trim()) return;\nfetch('/api/calendar/parse', {method:'POST', body: JSON.stringify({text: userInput.trim()})})","handlingStrategy":"validation","validationCode":"const text = (userInput ?? '').trim();\nif (!text) { showError('Enter a date expression to parse.'); return; }\nawait fetch(url, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({text})});","typeGuard":"function isValidParsePayload(b: unknown): b is {text: string} {\n  return typeof b === 'object' && b !== null && typeof (b as any).text === 'string' && (b as any).text.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Trim user input client-side and block blank submits","Keep the payload field name 'text' in sync between frontend and this route"],"tags":["validation","fastapi","calendar","client-error"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}