odysseus-dev/odysseus · error · HTTPException

Failed to update event

Error message

Failed to update event

What it means

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.

Source

Thrown at routes/calendar_routes.py:1315

                if data.all_day:
                    ev.is_utc = False  # all-day stays date-only
            if data.rrule is not None:
                ev.rrule = data.rrule
            if data.color is not None:
                ev.color = data.color if data.color else None
            is_caldav = ev.calendar and ev.calendar.source == "caldav"
            if is_caldav:
                ev.caldav_sync_pending = "update"
            db.commit()
            if is_caldav:
                await _push_caldav_event_after_commit(owner, base_uid, "update")
            return {"ok": True}
        except HTTPException:
            raise
        except Exception as e:
            db.rollback()
            logger.error("Failed to update event: %s", e)
            raise HTTPException(500, "Failed to update event")
        finally:
            db.close()

    @router.delete("/events/{uid}")
    async def delete_event(request: Request, uid: str, scope: str = "series"):
        owner = _require_user(request)
        try:
            base_uid = _resolve_base_uid(uid)
        except ValueError as e:
            raise HTTPException(400, str(e))
        db = SessionLocal()
        try:
            ev = _get_or_404_event(db, base_uid, owner)
            is_occurrence_delete = scope in {"occurrence", "instance"} and "::" in uid and bool(ev.rrule)
            is_caldav = ev.calendar and ev.calendar.source == "caldav"
            if is_occurrence_delete:
                key = _occurrence_exdate_key(uid, ev)
                if not key:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Read the logged exception ('Failed to update event: <e>') to pinpoint the field or DB operation failing.
  2. Send dtstart/dtend in ISO 8601 format when updating times.
  3. If the log shows lock contention, retry the update after concurrent sync/write activity completes.
  4. Run pending migrations if a column mismatch is reported.
Defensive patterns

Strategy: try-catch

Validate before calling

// only send changed fields, with ISO datetimes
const patch = {};
if (changed.summary) patch.summary = changed.summary;
if (changed.dtstart) { const d = new Date(changed.dtstart); if (!isNaN(d)) patch.dtstart = d.toISOString(); }
await api.put(`/events/${uid}`, patch);

Try / catch

try { await api.put(`/events/${uid}`, patch); }
catch (e) {
  if (e.status === 500) { /* server log 'Failed to update event' names the field/op */ throw e; }
  if (e.status === 404) { /* event deleted elsewhere */ refreshCalendar(); }
  else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/79b5fdbdf25de5d1. Report an issue: GitHub.