{"record":{"id":"348f98fb60b6c9e9","repo":"odysseus-dev/odysseus","slug":"failed-to-export-ics","errorCode":null,"errorMessage":"Failed to export ICS","messagePattern":"Failed to export ICS","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/calendar_routes.py","lineNumber":1615,"sourceCode":"                    lines.append(f\"RRULE:{ev.rrule}\")\n                lines.append(\"END:VEVENT\")\n            lines.append(\"END:VCALENDAR\")\n\n            ics_data = \"\\r\\n\".join(lines)\n            download_name = _safe_ics_filename(cal.name)\n            return Response(\n                content=ics_data,\n                media_type=\"text/calendar\",\n                headers={\n                    \"Content-Disposition\": f'attachment; filename=\"{download_name}\"',\n                    \"X-Content-Type-Options\": \"nosniff\",\n                },\n            )\n        except HTTPException:\n            raise\n        except Exception as e:\n            logger.error(\"Failed to export ICS: %s\", e)\n            raise HTTPException(500, \"Failed to export ICS\")\n        finally:\n            db.close()\n\n    @router.post(\"/quick-parse\")\n    async def quick_parse(request: Request):\n        \"\"\"Parse a natural-language event description into structured fields.\n\n        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","sourceCodeStart":1597,"sourceCodeEnd":1633,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1597-L1633","documentation":"Generic HTTP 500 from GET /export/{cal_id}. The handler loads the calendar via _get_or_404_calendar (404 for missing/foreign ids, re-raised untouched), then serializes its events into an ICS payload and wraps it in a text/calendar Response. Any non-HTTP exception during serialization — an event field that breaks ICS generation, timezone rendering failure, or a DB error mid-query — is logged as 'Failed to export ICS: <e>' and returned as this 500.","triggerScenarios":"An event with a stored datetime or timezone that the ICS writer cannot serialize (naive/aware mismatch, unknown TZID); a field containing characters the exporter cannot escape; database error while streaming the event rows; null in a column the export code assumes non-null.","commonSituations":"Exporting a calendar containing events created by an older version or by an import with edge-case data; tzdata missing in the deployment container; corrupt rows from a partially applied migration.","solutions":["Check the server log ('Failed to export ICS: <e>') for the failing event or field.","Inspect the calendar's events (GET /events for that range) for the odd-one-out — often a single event with unusual datetime/timezone data; fix or delete it.","Ensure tzdata is available in the runtime if the error names a timezone lookup.","Verify cal_id comes from the caller's own GET /calendars list (a bad id gives 404, not this 500)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const cals = await (await fetch('/calendars')).json();\nif (!cals.calendars.some(c => c.href === calId)) throw new NotFound('no such calendar'); // clean 404 path\nwindow.location = `/export/${calId}`;","typeGuard":null,"tryCatchPattern":"try { const blob = await api.get(`/export/${calId}`, {responseType: 'blob'}); saveBlob(blob); }\ncatch (e) {\n  if (e.status === 404) showError('Calendar not found');\n  else if (e.status === 500) showError('Export failed — server log names the bad event');\n}","preventionTips":["Use cal_id values from the caller's own GET /calendars list.","On 500, check the server log for the failing event and fix/delete it, then re-export.","Keep tzdata available in the runtime so datetime serialization of stored events never fails."],"tags":["http-500","ics","icalendar","fastapi","serialization"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}