{"record":{"id":"f7bd1375809f36e8","repo":"odysseus-dev/odysseus","slug":"failed-to-update-calendar","errorCode":null,"errorMessage":"Failed to update calendar","messagePattern":"Failed to update calendar","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/calendar_routes.py","lineNumber":1402,"sourceCode":"    @router.put(\"/calendars/{cal_id}\")\n    async def update_calendar(request: Request, cal_id: str, name: str = None, color: str = None):\n        owner = _require_user(request)\n        _reserve_calendar_uploads(request, color)\n        db = SessionLocal()\n        try:\n            cal = _get_or_404_calendar(db, cal_id, owner)\n            if name is not None:\n                cal.name = name\n            if color is not None:\n                cal.color = color\n            db.commit()\n            return {\"ok\": True}\n        except HTTPException:\n            raise\n        except Exception as e:\n            db.rollback()\n            logger.error(\"Failed to update calendar: %s\", e)\n            raise HTTPException(500, \"Failed to update calendar\")\n        finally:\n            db.close()\n\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:","sourceCodeStart":1384,"sourceCodeEnd":1420,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1384-L1420","documentation":"Generic HTTP 500 from PUT /calendars/{cal_id}. The handler loads the calendar via _get_or_404_calendar (which itself raises 404 for missing/foreign calendars), applies optional name/color updates, and commits. Non-HTTP exceptions — commit failure, DB constraint, oversized values — are rolled back, logged as 'Failed to update calendar: <e>', and returned as this 500. The 404 path is re-raised untouched, so this error always means the update itself failed, not a bad id.","triggerScenarios":"Setting name or color to a value the DB column rejects (excessive length, null bytes); database locked at commit; schema mismatch after upgrade; a color string not matching a CHECK constraint if one exists.","commonSituations":"Renaming to a very long pasted string; color picker emitting an unexpected format; concurrent CalDAV sync holding the write lock; migration not run so a referenced column is missing.","solutions":["Check the server log ('Failed to update calendar: <e>') for the concrete cause.","Keep name/color short and well-formed (e.g. '#rrggbb' for color).","For lock contention, retry once after other writes finish or enable SQLite WAL.","Run schema migrations if the log reports a missing column."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const patch = {};\nif (name != null) patch.name = String(name).trim().slice(0, 100);\nif (color != null && /^#[0-9a-fA-F]{6}$/.test(color)) patch.color = color;\nawait api.put(`/calendars/${calId}`, null, {params: patch});","typeGuard":null,"tryCatchPattern":"try { await api.put(`/calendars/${calId}`, null, {params: {name, color}}); }\ncatch (e) {\n  if (e.status === 404) refreshCalendarList(); // calendar gone — resync ids\n  else if (e.status === 500) throw e;          // check log 'Failed to update calendar'\n  else throw e;\n}","preventionTips":["Send only the query params you are changing (name/color); omitted params are left as-is.","Validate the color format client-side.","Distinguish 404 (bad/foreign id) from 500 (update failure) — they need different handling."],"tags":["http-500","fastapi","database","sqlalchemy"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}