{"record":{"id":"79b5fdbdf25de5d1","repo":"odysseus-dev/odysseus","slug":"failed-to-update-event","errorCode":null,"errorMessage":"Failed to update event","messagePattern":"Failed to update event","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/calendar_routes.py","lineNumber":1315,"sourceCode":"                if data.all_day:\n                    ev.is_utc = False  # all-day stays date-only\n            if data.rrule is not None:\n                ev.rrule = data.rrule\n            if data.color is not None:\n                ev.color = data.color if data.color else None\n            is_caldav = ev.calendar and ev.calendar.source == \"caldav\"\n            if is_caldav:\n                ev.caldav_sync_pending = \"update\"\n            db.commit()\n            if is_caldav:\n                await _push_caldav_event_after_commit(owner, base_uid, \"update\")\n            return {\"ok\": True}\n        except HTTPException:\n            raise\n        except Exception as e:\n            db.rollback()\n            logger.error(\"Failed to update event: %s\", e)\n            raise HTTPException(500, \"Failed to update event\")\n        finally:\n            db.close()\n\n    @router.delete(\"/events/{uid}\")\n    async def delete_event(request: Request, uid: str, scope: str = \"series\"):\n        owner = _require_user(request)\n        try:\n            base_uid = _resolve_base_uid(uid)\n        except ValueError as e:\n            raise HTTPException(400, str(e))\n        db = SessionLocal()\n        try:\n            ev = _get_or_404_event(db, base_uid, owner)\n            is_occurrence_delete = scope in {\"occurrence\", \"instance\"} and \"::\" in uid and bool(ev.rrule)\n            is_caldav = ev.calendar and ev.calendar.source == \"caldav\"\n            if is_occurrence_delete:\n                key = _occurrence_exdate_key(uid, ev)\n                if not key:","sourceCodeStart":1297,"sourceCodeEnd":1333,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1297-L1333","documentation":"Generic HTTP 500 from PUT /events/{uid}. After loading the event via _get_or_404_event and applying field updates (with datetime re-parsing for dtstart/dtend), the handler commits and may queue a CalDAV update push. Any non-HTTP exception — datetime parse failure, DB commit error, constraint violation — is rolled back, logged as 'Failed to update event: <e>', and returned as this 500. A missing/foreign event gives 404, not this error.","triggerScenarios":"dtstart/dtstart payload in a format _parse_dt_pair cannot handle; setting a field to a type the DB column rejects; database locked at commit; rrule/recurrence_exdates update producing invalid JSON; failure during the post-commit CalDAV push path.","commonSituations":"Client sending locale-formatted or empty datetimes on edit; concurrent edit from CalDAV sync colliding at commit; schema drift after an upgrade; editing an imported event whose stored fields interact badly with the update path.","solutions":["Read the logged exception ('Failed to update event: <e>') to pinpoint the field or DB operation failing.","Send dtstart/dtend in ISO 8601 format when updating times.","If the log shows lock contention, retry the update after concurrent sync/write activity completes.","Run pending migrations if a column mismatch is reported."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// only send changed fields, with ISO datetimes\nconst patch = {};\nif (changed.summary) patch.summary = changed.summary;\nif (changed.dtstart) { const d = new Date(changed.dtstart); if (!isNaN(d)) patch.dtstart = d.toISOString(); }\nawait api.put(`/events/${uid}`, patch);","typeGuard":null,"tryCatchPattern":"try { await api.put(`/events/${uid}`, patch); }\ncatch (e) {\n  if (e.status === 500) { /* server log 'Failed to update event' names the field/op */ throw e; }\n  if (e.status === 404) { /* event deleted elsewhere */ refreshCalendar(); }\n  else throw e;\n}","preventionTips":["Send only modified fields — smaller payloads expose fewer parse paths.","Use ISO 8601 for any datetime field you update.","Distinguish 404 (event gone) from 500 (update failed) in error handling; they need different client responses."],"tags":["http-500","fastapi","database","datetime"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}