odysseus-dev/odysseus · warning · HTTPException

Event not found

Error message

Event not found

What it means

HTTP 404 raised by _get_or_404_event when no CalendarEvent row matches the given uid (joined to its calendar). Raised before any ownership check, so it covers both nonexistent and malformed uids.

Source

Thrown at routes/calendar_routes.py:100

def _get_or_404_calendar(db, cal_id: str, owner: str) -> CalendarCal:
    cal = db.query(CalendarCal).filter(CalendarCal.id == cal_id).first()
    if not cal:
        raise HTTPException(404, "Calendar not found")
    # Tighten the legacy null-owner gate (v2 review HIGH-12): if the
    # caller is authenticated AND the calendar's owner is null OR
    # belongs to a different user, treat it as not-found. The previous
    # rule (`if cal.owner and cal.owner != owner`) silently allowed any
    # authenticated user to read/edit any calendar with owner=None.
    if owner and (cal.owner is None or cal.owner != owner):
        raise HTTPException(404, "Calendar not found")
    return cal


def _get_or_404_event(db, uid: str, owner: str) -> CalendarEvent:
    ev = db.query(CalendarEvent).join(CalendarCal).filter(CalendarEvent.uid == uid).first()
    if not ev:
        raise HTTPException(404, "Event not found")
    cal = ev.calendar
    if owner and cal and (cal.owner is None or cal.owner != owner):
        raise HTTPException(404, "Event not found")
    return ev


def _ics_escape(text: str) -> str:
    """Escape a value for an iCalendar TEXT field (RFC 5545 §3.3.11).

    Backslash, semicolon and comma are structural in TEXT values and must be
    escaped, and newlines become a literal ``\\n``. Backslash is escaped first
    so the escapes we add aren't re-escaped.
    """
    return (
        (text or "")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Refresh events from the server and retry with a current uid.
  2. For recurring events, resolve the base uid with _resolve_base_uid semantics (:: strips the date suffix) before calling event endpoints.
  3. Handle 404 by removing the stale event from local UI state.
Defensive patterns

Strategy: validation

Validate before calling

function baseUid(uid) {
  const i = uid.indexOf('::');
  const base = i === -1 ? uid : uid.slice(0, i);
  if (!base) throw new Error('bad uid');
  return base;
}

Try / catch

catch (e) { if (e.status === 404) { removeLocalEvent(uid); } else throw e; }

Prevention

When it happens

Trigger: Any /api/calendar event route with a uid not in the events table: deleted event, occurrence uid from a different instance, truncated uid in a URL.

Common situations: Editing an event that was deleted from another client; stale UI state; compound recurrence uids passed where the base uid is stored (or vice versa).

Related errors


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