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
- Refresh events from the server and retry with a current uid.
- For recurring events, resolve the base uid with _resolve_base_uid semantics (:: strips the date suffix) before calling event endpoints.
- 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
- For recurring occurrences, send the compound uid exactly as the server returned it.
- Refresh events before editing ones that may have changed elsewhere.
- Drop 404-ing events from local state instead of retrying.
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.