Skip to content

Commit 6287026

Browse files
fix: prevent 500 error when deleting calendar events with empty uid (#27500)
* fix: prevent 500 error when deleting calendar events with empty uid This fix addresses a 500 error that occurs when trying to delete a Google Calendar event with an empty event ID, which results in a malformed API URL. Root cause: When calendar event creation fails or returns without an ID, booking references were being created with empty uids. Later, when trying to delete these events (e.g., during reschedule), the empty uid caused a 404 from Google Calendar API which was then thrown as a 500 error. Changes: 1. CalendarManager.deleteEvent: Added validation to skip deletion if bookingRefUid is empty, with appropriate error logging. This is a safety net for existing bad data in the database. 2. EventManager.create: Added filtering to exclude booking references with empty uids from being stored, with error logging to help diagnose the root cause of missing event IDs. 3. EventManager.updateLocation: Same filtering applied for consistency. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: revert EventManager filtering to preserve existing behavior The test expects that booking references with empty uids are still created when calendar event creation fails. This is intentional behavior to track failed calendar syncs. The CalendarManager.deleteEvent fix handles the deletion case gracefully by skipping the API call when uid is empty. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: throw ErrorWithCode instead of silently returning when bookingRefUid is empty Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: validate uid before calling calendar.deleteEvent to prevent 500 error - Add uid check in lastAttendeeDeleteBooking.ts before calling calendar.deleteEvent - Add uid check in EventManager.updateAllCalendarEvents before calling calendar.deleteEvent - Revert CalendarManager.deleteEvent changes (validation at call sites is the proper fix) Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: add error logging safeguard in CalendarManager.deleteEvent for empty bookingRefUid Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: move bookingRefUid check before getCalendar call Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * fix: prevent storing booking references with empty uid when calendar event creation fails Root cause fix: Change the check from 'if (createdEvent)' to 'if (createdEvent.createdEvent)' in createAllCalendarEvents to prevent failed calendar events from being added to results. This prevents empty uids from being stored in the database in the first place, which was causing 500 errors when trying to delete non-existent calendar events later. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * test: update test to expect no calendar reference when calendar event creation fails The root cause fix changes behavior so that failed calendar events no longer create booking references with empty uids. This test now expects an empty references array when calendar event creation fails, which is the correct behavior that prevents 500 errors later when trying to delete non-existent calendar events. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: use createdEvent.success instead of createdEvent.createdEvent Using success is more semantically clear since it explicitly checks the success status rather than checking if the nested object exists. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * refactor: use optional chaining for createdEvent?.success Using optional chaining is safer in case createdEvent is somehow undefined. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * revert: remove root cause fix to preserve backfilling script compatibility The root cause fix (checking createdEvent?.success instead of createdEvent) would break the backfilling script that relies on booking references with empty uids to identify which bookings need backfilling. This PR now only includes defensive checks to prevent 500 errors when deleting calendar events with empty uids. Co-Authored-By: benny@cal.com <sldisek783@gmail.com> * revert * fix --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent 96bec9f commit 6287026

2 files changed

Lines changed: 52 additions & 4 deletions

File tree

packages/features/calendars/lib/CalendarManager.test.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
import { prisma } from "@calcom/prisma/__mocks__/prisma";
2-
3-
import { describe, expect, it, vi, beforeEach } from "vitest";
4-
2+
import type { CalendarEvent } from "@calcom/types/Calendar";
3+
import { beforeEach, describe, expect, it, vi } from "vitest";
54
import {
6-
getCalendarCredentials,
75
deduplicateCredentialsBasedOnSelectedCalendars,
6+
deleteEvent,
7+
getCalendarCredentials,
88
processEvent,
99
} from "./CalendarManager";
10+
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
1011

1112
vi.mock("@calcom/prisma", () => ({
1213
prisma,
1314
}));
1415

16+
vi.mock("@calcom/app-store/_utils/getCalendar", () => ({
17+
getCalendar: vi.fn(),
18+
}));
19+
1520
vi.mock("@calcom/lib/constants", () => ({
1621
ORGANIZER_EMAIL_EXEMPT_DOMAINS: "",
1722
IS_PRODUCTION: false,
@@ -560,4 +565,37 @@ describe("CalendarManager tests", () => {
560565
expect(uniqueCredentials).toEqual(credentials);
561566
});
562567
});
568+
569+
describe("fn: deleteEvent", () => {
570+
beforeEach(() => {
571+
vi.clearAllMocks();
572+
});
573+
574+
it("should return early when bookingRefUid is empty string", async () => {
575+
vi.mocked(getCalendar).mockResolvedValue({ deleteEvent: vi.fn() } as any);
576+
577+
const credential = {
578+
id: 1,
579+
type: "google_calendar",
580+
key: { access_token: "test_token" },
581+
encryptedKey: null,
582+
userId: 1,
583+
user: { email: "test@example.com" },
584+
teamId: null,
585+
appId: "google-calendar",
586+
invalid: false,
587+
delegationCredentialId: null,
588+
delegatedTo: null,
589+
};
590+
591+
const result = await deleteEvent({
592+
credential,
593+
bookingRefUid: "",
594+
event: buildCalendarEvent(),
595+
externalCalendarId: "calendar-id",
596+
});
597+
598+
expect(result).toEqual({});
599+
});
600+
});
563601
});

packages/features/calendars/lib/CalendarManager.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,16 @@ export const deleteEvent = async ({
546546
event: CalendarEvent;
547547
externalCalendarId?: string | null;
548548
}): Promise<unknown> => {
549+
if (!bookingRefUid) {
550+
log.error(
551+
"deleteEvent failed - bookingRefUid is empty, skipping calendar deletion to prevent malformed API request",
552+
safeStringify({
553+
credential: getPiiFreeCredential(credential),
554+
event: getPiiFreeCalendarEvent(event),
555+
})
556+
);
557+
return Promise.resolve({});
558+
}
549559
const calendar = await getCalendar(credential, "booking");
550560
log.debug(
551561
"Deleting calendar event",

0 commit comments

Comments
 (0)