{"record":{"id":"cf8b8a4cfc71f8d3","repo":"odysseus-dev/odysseus","slug":"invalid-ics-file-e","errorCode":null,"errorMessage":"Invalid ICS file: {e}","messagePattern":"Invalid ICS file: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"routes/calendar_routes.py","lineNumber":1423,"sourceCode":"\n\n    # Hard cap on ICS upload (ICS_MAX_BYTES, default 10 MB). Loading the whole\n    # file into memory is unavoidable with python-icalendar, so an unbounded\n    # upload would OOM.\n\n    @router.post(\"/import\")\n    async def import_ics(request: Request, file: UploadFile = File(...), calendar_name: str = \"\"):\n        \"\"\"Import events from an .ics file (scoped to caller's account).\"\"\"\n        from icalendar import Calendar as iCal\n\n        owner = _require_user(request)\n        db = SessionLocal()\n        try:\n            content = await read_upload_limited(file, ICS_MAX_BYTES, \"ICS file\")\n            try:\n                cal_data = iCal.from_ical(content)\n            except Exception as e:\n                raise HTTPException(400, f\"Invalid ICS file: {e}\")\n\n            # Sanitize display name — length cap + strip control chars\n            raw_name = calendar_name.strip() or (file.filename or \"\").replace(\".ics\", \"\").replace(\"_\", \" \").strip() or \"Imported\"\n            cal_display = \"\".join(c for c in raw_name if c.isprintable())[:120] or \"Imported\"\n\n            target_cal = db.query(CalendarCal).filter(\n                CalendarCal.name == cal_display,\n                CalendarCal.owner == owner,\n            ).first()\n            if not target_cal:\n                target_cal = CalendarCal(\n                    id=str(uuid.uuid4()),\n                    owner=owner,\n                    name=cal_display,\n                    color=\"#7c4dff\",\n                    source=\"import\",\n                )\n                db.add(target_cal)","sourceCodeStart":1405,"sourceCodeEnd":1441,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1405-L1441","documentation":"Raised as HTTP 400 by POST /import when icalendar's Calendar.from_ical() throws while parsing the uploaded file. from_ical raises ValueError (or Index error) on content that is not a valid iCalendar stream: missing BEGIN:VCALENDAR/END:VCALENDAR wrapper, bare vEvent without a calendar, invalid line syntax, or decoding problems. The exception text is embedded in the 400 detail ('Invalid ICS file: <e>'). Note the read itself is size-limited beforehand (ICS_MAX_BYTES, default 10 MB), so oversize files fail earlier with a different message.","triggerScenarios":"Uploading a .csv or .vcs file renamed to .ics; a text export missing the BEGIN:VCALENDAR header; a truncated file (upload interrupted); an HTML error page saved as .ics; non-UTF-8 bytes that break the parser.","commonSituations":"Users exporting from Google/Apple Calendar getting a partial download; corporate proxies serving an HTML login page instead of the file; hand-edited ICS with folded lines broken incorrectly; files over 10 MB hitting the separate size-limit error instead.","solutions":["Open the file in a text editor and confirm it starts with BEGIN:VCALENDAR and ends with END:VCALENDAR.","Re-export the calendar from the source application rather than hand-editing.","Ensure the upload is the raw bytes (Content-Type multipart/form-data, file field 'file') and under ICS_MAX_BYTES (10 MB default).","Check the 400 detail — icalendar's exception text usually points at the offending line."],"exampleFix":"# before\n# file contents: \"Subject,Start\nLunch,1pm\"  (a CSV renamed to .ics)\ncurl -F file=@events.csv /import\n\n# after\n# file contents: \"BEGIN:VCALENDAR\\r\\nVERSION:2.0\\r\\nBEGIN:VEVENT\\r\\n...\\r\\nEND:VCALENDAR\"\ncurl -F file=@calendar.ics /import","handlingStrategy":"validation","validationCode":"function looksLikeIcs(text) {\n  return /BEGIN:VCALENDAR/.test(text.slice(0, 200)) && /END:VCALENDAR/.test(text.slice(-200));\n}\nconst text = await file.text();\nif (!looksLikeIcs(text)) throw new Error('Not a valid iCalendar file');\nawait upload(file); // parse errors avoided before the request","typeGuard":null,"tryCatchPattern":"try { await api.post('/import', formData); }\ncatch (e) {\n  if (e.status === 400) showError(e.detail); // detail embeds icalendar's parse reason\n  else throw e;\n}","preventionTips":["Accept only .ics files in the file picker and check the BEGIN:VCALENDAR header client-side.","Keep files under ICS_MAX_BYTES (10 MB) — oversize uploads fail earlier with a size error.","Re-export from the source calendar rather than editing ICS by hand."],"tags":["ics","icalendar","http-400","parsing","fastapi"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}