{"record":{"id":"47010dcc09c2bc7f","repo":"odysseus-dev/odysseus","slug":"failed-to-delete-event","errorCode":null,"errorMessage":"Failed to delete event","messagePattern":"Failed to delete event","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"routes/calendar_routes.py","lineNumber":1357,"sourceCode":"                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, \"scope\": \"occurrence\", \"exdate\": key}\n            if is_caldav:\n                _record_caldav_delete_tombstone(db, ev, owner)\n            db.delete(ev)\n            db.commit()\n            if is_caldav:\n                await _push_caldav_event_after_commit(owner, base_uid, \"delete\")\n            return {\"ok\": True}\n        except HTTPException:\n            raise\n        except Exception as e:\n            db.rollback()\n            logger.error(\"Failed to delete event: %s\", e)\n            raise HTTPException(500, \"Failed to delete event\")\n        finally:\n            db.close()\n\n    @router.post(\"/calendars\")\n    async def create_calendar(request: Request, name: str = \"Imported\", color: str = \"#5b8abf\"):\n        owner = _require_user(request)\n        _reserve_calendar_uploads(request, color)\n        db = SessionLocal()\n        try:\n            cal = CalendarCal(\n                id=str(uuid.uuid4()),\n                owner=owner,\n                name=name,\n                color=color,\n                source=\"local\",\n            )\n            db.add(cal)\n            db.commit()","sourceCodeStart":1339,"sourceCodeEnd":1375,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L1339-L1375","documentation":"Generic HTTP 500 from DELETE /events/{uid}. The handler loads the event, optionally records a CalDAV delete tombstone and appends an EXDATE for occurrence deletes, deletes the row, commits, and may queue a CalDAV delete push. Any non-HTTP exception — DB commit failure, malformed stored recurrence_exdates JSON, tombstone insert error — is rolled back, logged as 'Failed to delete event: <e>', and returned as this 500.","triggerScenarios":"Database locked at commit while CalDAV sync runs concurrently; recurrence_exdates JSON corrupt so _recurrence_exdates throws during an occurrence delete; tombstone write violating a constraint; DB file/schema unavailable.","commonSituations":"Double-delete racing itself (two rapid clicks both past the 404 point); corrupt exdate data from an older import; schema drift after upgrade; SQLite contention with the background CalDAV push worker.","solutions":["Check the server log ('Failed to delete event: <e>') for the underlying exception.","If stored recurrence_exdates JSON is corrupt, repair or clear the value on that CalendarEvent row, then retry the delete.","For 'database is locked', retry after concurrent writes finish or enable WAL.","Guard the UI against double-submission of the delete button."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// confirm the event still exists to get the clean 404 instead of a 500 race\nconst ev = await api.get(`/events?start=...&end=...`); // or a single-event fetch\nif (!eventExists(ev, uid)) return; // already gone","typeGuard":null,"tryCatchPattern":"try { await api.del(`/events/${uid}`, {params: {scope}}); }\ncatch (e) {\n  if (e.status === 404) return;            // already deleted\n  if (e.status === 500) {                   // transient lock — one retry\n    await sleep(300);\n    return api.del(`/events/${uid}`, {params: {scope}});\n  }\n  throw e;\n}","preventionTips":["Treat 404 on repeat deletes as success and 500 as 'check log, retry once'.","Disable the delete control while the request is in flight to avoid racing duplicates.","Keep recurrence_exdates data consistent — corrupt stored JSON makes occurrence deletes 500."],"tags":["http-500","fastapi","database","caldav"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}