{"record":{"id":"aec26833521d48b8","repo":"calcom/cal.diy","slug":"failed-to-update-event","errorCode":null,"errorMessage":"Failed to update event","messagePattern":"Failed to update event","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts","lineNumber":314,"sourceCode":"    }\n  }\n\n  private async updateEventWithClient(\n    calendar: calendar_v3.Calendar,\n    calendarId: string,\n    eventId: string,\n    updateData: UpdateUnifiedCalendarEventInput\n  ): Promise<GoogleCalendarEventResponse> {\n    const effectiveCalendarId = calendarId || \"primary\";\n    const updatePayload = new GoogleCalendarEventInputPipe().transform(updateData);\n    try {\n      const event = await calendar.events.patch({\n        calendarId: effectiveCalendarId,\n        eventId,\n        requestBody: updatePayload,\n      });\n      if (!event.data) {\n        throw new NotFoundException(\"Failed to update event\");\n      }\n      return event.data as GoogleCalendarEventResponse;\n    } catch (error) {\n      if (error instanceof HttpException) throw error;\n      throw this.mapGoogleApiError(error, \"Failed to update event details\");\n    }\n  }\n\n  private async deleteEventWithClient(\n    calendar: calendar_v3.Calendar,\n    calendarId: string,\n    eventId: string\n  ): Promise<void> {\n    const effectiveCalendarId = calendarId || \"primary\";\n    try {\n      await calendar.events.delete({\n        calendarId: effectiveCalendarId,\n        eventId,","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts#L296-L332","documentation":"Thrown by updateEventWithClient when calendar.events.patch resolves with no data. The message string says 'Failed to update event' and is mapped to NotFoundException (HTTP 404) — note the status code does not match the failure semantics: an empty patch response usually indicates the event/calendarId combo was not found rather than a not-permitted update. The catch block also routes Google 404s here.","triggerScenarios":"Calling PATCH /v2/calendars/connections/{id}/events/{eventId} with an eventId that does not exist in the calendarId, or a calendarId that does not exist. The patch target must resolve before any field is applied.","commonSituations":"Event deleted between read and update (TOCTOU); client sent the recurring-event master id but the calendar only has instances; calendarId typo; event migrated to another calendar.","solutions":["Re-fetch the event to confirm it still exists before patching.","Use the instance id (not master id) for recurring event updates.","If the event was deleted, abandon the update and surface 'event no longer exists' to the user.","Consider switching the exception type to BadRequestException or InternalServerErrorException to better match 'patch returned no data' semantics."],"exampleFix":"// before\nconst event = await calendar.events.patch({ calendarId, eventId, requestBody: updatePayload });\nif (!event.data) {\n  throw new NotFoundException('Failed to update event');\n}\n\n// after: distinguish not-found from empty-response\nif (!event.data) {\n  this.logger.warn('Patch returned no data', { calendarId, eventId });\n  throw new NotFoundException(`Event ${eventId} not found in calendar ${calendarId}`);\n}","handlingStrategy":"try-catch","validationCode":"// Pre-flight existence check before patching (trades one extra call for clarity).\nasync function eventExistsForPatch(googleCalendarService: GoogleCalendarService, userId: number, credId: number, calId: string, evId: string): Promise<boolean> {\n  try {\n    await googleCalendarService.getEventByConnectionId(userId, credId, calId, evId);\n    return true;\n  } catch (e) {\n    if (e instanceof NotFoundException) return false;\n    throw e;\n  }\n}","typeGuard":"function isPatchSuccess(r: { data?: unknown }): r is { data: Record<string, unknown> } {\n  return Boolean(r.data && typeof r.data === 'object');\n}","tryCatchPattern":"try {\n  return await googleCalendarService.updateEventByConnectionId(userId, credId, calId, evId, updateData);\n} catch (e) {\n  if (e instanceof NotFoundException && /update event/i.test(e.message)) {\n    // event no longer exists — abandon update\n    return null;\n  }\n  throw e;\n}","preventionTips":["Re-fetch the event immediately before patching in read-modify-write flows to avoid TOCTOU.","Use the recurring-instance id, not the master id, when updating a single occurrence.","Consider changing the thrown exception type to better match the empty-response semantics."],"tags":["google-calendar","google-api","event-update","nestjs","not-found"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}