{"record":{"id":"adb25e060fc56718","repo":"odysseus-dev/odysseus","slug":"calendar-not-found","errorCode":null,"errorMessage":"Calendar not found","messagePattern":"Calendar not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"routes/calendar_routes.py","lineNumber":86,"sourceCode":"\ndef _require_user(request: Request) -> str:\n    \"\"\"Return the authenticated user. Uses require_user so AUTH_ENABLED=false\n    and single-user mode both work: require_user returns \"\" when auth is\n    disabled or unconfigured, and only raises 401 when auth is configured but\n    the caller is unauthenticated. Falls back to FALLBACK_OWNER for calendar\n    writes so data isn't stored under an empty owner in single-user mode.\"\"\"\n    user = require_user(request)\n    if user:\n        return user\n    # require_user returned \"\" — auth is off or unconfigured (single-user).\n    # Use FALLBACK_OWNER so calendar rows have a stable owner for filtering.\n    return FALLBACK_OWNER\n\n\ndef _get_or_404_calendar(db, cal_id: str, owner: str) -> CalendarCal:\n    cal = db.query(CalendarCal).filter(CalendarCal.id == cal_id).first()\n    if not cal:\n        raise HTTPException(404, \"Calendar not found\")\n    # Tighten the legacy null-owner gate (v2 review HIGH-12): if the\n    # caller is authenticated AND the calendar's owner is null OR\n    # belongs to a different user, treat it as not-found. The previous\n    # rule (`if cal.owner and cal.owner != owner`) silently allowed any\n    # authenticated user to read/edit any calendar with owner=None.\n    if owner and (cal.owner is None or cal.owner != owner):\n        raise HTTPException(404, \"Calendar not found\")\n    return cal\n\n\ndef _get_or_404_event(db, uid: str, owner: str) -> CalendarEvent:\n    ev = db.query(CalendarEvent).join(CalendarCal).filter(CalendarEvent.uid == uid).first()\n    if not ev:\n        raise HTTPException(404, \"Event not found\")\n    cal = ev.calendar\n    if owner and cal and (cal.owner is None or cal.owner != owner):\n        raise HTTPException(404, \"Event not found\")\n    return ev","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/calendar_routes.py#L68-L104","documentation":"HTTP 404 raised by the calendar helper _get_or_404_calendar when db.query(CalendarCal).filter(id == cal_id).first() finds no row. Every calendar route funnels through this helper, so an unknown or malformed calendar id yields 404 regardless of ownership.","triggerScenarios":"Any /api/calendar route taking a cal_id that does not exist in the calendars table: deleted calendar, stale id from an old client, or a typo in the path.","commonSituations":"Client caches a calendar list and uses an id deleted elsewhere; DB reset/restore dropped rows; two devices out of sync after offline edits.","solutions":["Re-fetch the calendar list (the collection route) and use a current id.","Treat 404 as 'deleted elsewhere' and refresh local state rather than retrying.","If the calendar should exist, verify the DB the app points at is the expected one."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const ids = new Set((await listCalendars()).map(c => c.id));\nif (!ids.has(calId)) throw new Error('calendar deleted — refresh');","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.status === 404) { dropLocalCalendar(calId); } else throw e; }","preventionTips":["Sync calendar ids from the server before operations on possibly-stale data.","On 404 remove the calendar from local cache instead of retrying."],"tags":["not-found","http-404","calendar","crud","fastapi"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}