{"record":{"id":"15cbfa4675b9625c","repo":"RocketChat/Rocket.Chat","slug":"invalid-calendar-event","errorCode":null,"errorMessage":"invalid-calendar-event","messagePattern":"invalid-calendar-event","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/calendar.ts","lineNumber":182,"sourceCode":"API.v1.post(\n\t'calendar-events.update',\n\t{\n\t\tauthRequired: true,\n\t\tbody: isCalendarEventUpdateProps,\n\t\tresponse: {\n\t\t\t200: successSchema,\n\t\t\t400: validateBadRequestErrorResponse,\n\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t},\n\t},\n\tasync function action() {\n\t\tconst { userId } = this;\n\t\tconst { eventId, startTime, endTime, subject, description, meetingUrl, reminderMinutesBeforeStart, busy } = this.bodyParams;\n\n\t\tconst event = await Calendar.get(eventId);\n\n\t\tif (event?.uid !== userId) {\n\t\t\tthrow new Error('invalid-calendar-event');\n\t\t}\n\n\t\tawait Calendar.update(eventId, {\n\t\t\tstartTime: new Date(startTime),\n\t\t\t...(endTime && { endTime: new Date(endTime) }),\n\t\t\tsubject,\n\t\t\tdescription,\n\t\t\tmeetingUrl,\n\t\t\treminderMinutesBeforeStart,\n\t\t\t...(typeof busy === 'boolean' && { busy }),\n\t\t});\n\n\t\treturn API.v1.success();\n\t},\n);\n\nAPI.v1.post(\n\t'calendar-events.delete',","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/calendar.ts#L164-L200","documentation":"Thrown by the calendar event update endpoint when the authenticated user does not own the targeted event. The server calls Calendar.get(eventId) and compares event.uid against this.userId; the optional-chaining check (event?.uid) means a missing/non-existent event also trips this guard, so the error conflates 'not found' and 'not owned'. It is a plain Error (no Meteor error code), so callers cannot distinguish ownership failure from a stale ID.","triggerScenarios":"POST to the calendar update endpoint with an eventId that (a) does not exist in the Calendar collection, or (b) exists but has event.uid !== the calling user's _id. Also fires if the user's session userId differs from the event owner (e.g., after account switch).","commonSituations":"Using a stale eventId cached client-side after the event was deleted or reassigned; passing an eventId from another user's calendar; integration tests that reuse fixtures across users.","solutions":["Verify the event exists and is owned by the current user before calling update (fetch via Calendar.get and check uid).","Refresh the client's event list to discard stale/deleted eventIds before allowing an edit.","If building tooling that edits others' events, add a server-side privileged path or admin permission check rather than reusing this endpoint."],"exampleFix":"// before\nconst event = await Calendar.get(eventId);\nif (event?.uid !== userId) {\n  throw new Error('invalid-calendar-event');\n}\n\n// after - distinguish missing vs. not-owned for clearer client handling\nconst event = await Calendar.get(eventId);\nif (!event) {\n  throw new Meteor.Error('error-calendar-event-not-found', 'Calendar event not found');\n}\nif (event.uid !== userId) {\n  throw new Meteor.Error('error-calendar-event-not-owned', 'You do not own this calendar event');\n}","handlingStrategy":"validation","validationCode":"// Before calling the calendar update endpoint, confirm ownership\nasync function canUpdateCalendarEvent(userId, eventId) {\n  const event = await Calendar.get(eventId);\n  return Boolean(event && event.uid === userId);\n}\n// usage\nif (!(await canUpdateCalendarEvent(currentUserId, eventId))) {\n  throw new Error('Refusing update: event missing or not owned');\n}","typeGuard":"function isOwnedCalendarEvent(event, userId) {\n  return Boolean(event) && typeof event.uid === 'string' && event.uid === userId;\n}","tryCatchPattern":"try {\n  await api.updateCalendarEvent(eventId, patch);\n} catch (e) {\n  if (e.message === 'invalid-calendar-event') {\n    // event missing or not owned - refresh list, do not blind-retry\n    await refreshCalendar();\n    return;\n  }\n  throw e;\n}","preventionTips":["Cache eventId alongside its owner uid and re-check before edit calls.","Discard eventIds when the user session changes.","Distinguish 'not found' from 'not owned' in your own wrapper to avoid masking bugs."],"tags":["calendar","authorization","ownership","rest-api"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}