calcom/cal.diy · error · BadRequestException

Can't cancel booking with uid=${bookingUid} because it has b

Error message

Can't cancel booking with uid=${bookingUid} because it has been cancelled already. Please provide uid of a booking that is not cancelled.

What it means

cancelBooking checks bookingInfo.status === 'CANCELLED' and throws BadRequestException (HTTP 400). Cancelling an already-cancelled booking is not permitted; the caller must supply the uid of an active booking.

Source

Thrown at apps/api/v2/src/platform/bookings/2024-04-15/controllers/bookings.controller.ts:255

    @Param("bookingUid") bookingUid: string,
    @Body() _body: CancelBookingInput_2024_04_15,
    @Headers(X_CAL_CLIENT_ID) clientId?: string,
    @Headers(X_CAL_PLATFORM_EMBED) isEmbed?: string
  ): Promise<ApiResponse<{ bookingId: number; bookingUid: string; onlyRemovedAttendee: boolean }>> {
    const oAuthClientId = clientId?.toString();
    const isUidNumber = !Number.isNaN(Number(bookingUid));

    if (isUidNumber) {
      throw new BadRequestException("Please provide booking uid instead of booking id.");
    }

    if (bookingUid) {
      const { bookingInfo } = await getBookingInfo(bookingUid);
      if (!bookingInfo) {
        throw new NotFoundException(`Booking with UID=${bookingUid} does not exist.`);
      }
      if (bookingInfo.status === "CANCELLED") {
        throw new BadRequestException(
          `Can't cancel booking with uid=${bookingUid} because it has been cancelled already. Please provide uid of a booking that is not cancelled.`
        );
      }
      try {
        req.body.uid = bookingUid;
        const bookingRequest = await this.createNextApiBookingRequest(req, oAuthClientId, undefined, isEmbed);
        const res = await handleCancelBooking({
          bookingData: bookingRequest.body,
          userId: bookingRequest.userId,
          userUuid: bookingRequest.userUuid,
          arePlatformEmailsEnabled: bookingRequest.arePlatformEmailsEnabled,
          platformClientId: bookingRequest.platformClientId,
          platformCancelUrl: bookingRequest.platformCancelUrl,
          platformRescheduleUrl: bookingRequest.platformRescheduleUrl,
          platformBookingUrl: bookingRequest.platformBookingUrl,
          actionSource: "API_V2",
        });
        return {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Fetch the booking status first and skip cancel if already CANCELLED.
  2. Make cancel idempotent client-side by treating 'already cancelled' (HTTP 400 with this message) as success.
  3. Disable the cancel action in the UI once status is CANCELLED.

Example fix

// before
await api.cancelBooking(uid);
// after
const b = await api.getBooking(uid);
if (b.status === 'CANCELLED') return; // idempotent
await api.cancelBooking(uid);
Defensive patterns

Strategy: validation

Validate before calling

async function cancelIfActive(api, uid) {
  const b = await api.getBooking(uid);
  if (b.status === 'CANCELLED') return; // idempotent
  return api.cancelBooking(uid);
}

Type guard

const isAlreadyCancelled = (b: { status: string }): boolean =>
  b.status === 'CANCELLED';

Try / catch

try { await api.cancelBooking(uid); }
catch (e) {
  if (e.status === 400 && /has been cancelled already/.test(e.message)) return; // idempotent
  throw e;
}

Prevention

When it happens

Trigger: POST /:bookingUid/cancel for a booking whose status is already 'CANCELLED'.

Common situations: Double-submit; retry after a successful cancel; UI not refreshing booking state; concurrent cancel requests.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/df593d969afe98f5. Report an issue: GitHub.