calcom/cal.diy · error · NotFoundException

Booking with uid ${uid} not found

Error message

Booking with uid ${uid} not found

What it means

Thrown by EventTypesAtomService.getUserPaymentInfo when the payment record exists (rawPayment is truthy) but its booking relation is null. This means the payment row has a null or missing foreign key to the booking table, indicating a data integrity issue. The payment was found but is orphaned — no booking is associated with it, so the start/end times and event type cannot be resolved.

Source

Thrown at apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts:321

              isInstalled: !!userCredentialIds.length || !!teams.length || app.isGlobal,
              isSetupAlready,
              ...(app.dependencies && { dependencyData }),
            };
          }
        )
    );
    return apps[0];
  }

  async getUserPaymentInfo(uid: string) {
    const rawPayment = await this.atomsRepository.getRawPayment(uid);
    if (!rawPayment) throw new NotFoundException(`Payment with uid ${uid} not found`);
    const { data, booking: _booking, ...restPayment } = rawPayment;
    const payment = {
      ...restPayment,
      data: data as Record<string, unknown>,
    };
    if (!_booking) throw new NotFoundException(`Booking with uid ${uid} not found`);
    const { startTime, endTime, eventType, ...restBooking } = _booking;
    const booking = {
      ...restBooking,
      startTime: startTime.toString(),
      endTime: endTime.toString(),
    };
    if (!eventType) throw new NotFoundException(`Event type with uid ${uid} not found`);
    if (eventType.users.length === 0 && !eventType.team)
      throw new NotFoundException(`No users found or no team present for event type with uid ${uid}`);
    const [user] = eventType?.users.length
      ? eventType.users
      : [{ name: null, theme: null, hideBranding: null, username: null }];
    const profile = {
      name: eventType.team?.name || user?.name || null,
      theme: (!eventType.team?.name && user?.theme) || null,
      hideBranding: eventType.team?.hideBranding || user?.hideBranding || null,
    };
    return {

View on GitHub (pinned to 176037d0af)

Solutions

  1. Investigate the payment record in the database: check if bookingId is null or references a non-existent booking.
  2. If this is a timing issue (webhook race), retry after a short delay to see if the booking becomes available.
  3. Run a data integrity check: SELECT p.uid, p.bookingId, b.id FROM payment p LEFT JOIN booking b ON p.bookingId = b.id WHERE b.id IS NULL.
  4. If the booking was legitimately deleted, remove the orphaned payment record or re-associate it.

Example fix

// before: no retry on transient missing booking
const info = await api.get(`/v2/atoms/payment-info/${paymentUid}`);

// after: retry with backoff for webhook race conditions
const fetchWithRetry = async (uid, retries = 3) => {
  for (let i = 0; i < retries; i++) {
    try {
      return await api.get(`/v2/atoms/payment-info/${uid}`);
    } catch (e) {
      if (e.statusCode === 404 && i < retries - 1) {
        await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        continue;
      }
      throw e;
    }
  }
};
Defensive patterns

Strategy: retry

Validate before calling

// For webhook-driven flows, verify the booking exists before querying payment info
const verifyBookingExists = async (api: ApiClient, paymentUid: string): Promise<void> => {
  const payment = await api.get(`/v2/payments/${paymentUid}`);
  if (!payment?.bookingId) {
    throw new Error('Payment exists but has no associated booking. Possible data integrity issue.');
  }
};

Try / catch

// Retry for webhook race conditions where booking hasn't committed yet
const getPaymentInfoWithRetry = async (uid: string, maxRetries = 3): Promise<any> => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await api.get(`/v2/atoms/payment-info/${uid}`);
    } catch (err: any) {
      const msg = err?.response?.data?.message ?? '';
      if (err?.response?.status === 404 && msg.includes('Booking') && i < maxRetries - 1) {
        await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        continue;
      }
      throw err;
    }
  }
};

Prevention

When it happens

Trigger: A payment record exists in the database with a null bookingId or a bookingId referencing a deleted booking. The payment was created by a webhook before the booking was committed (race condition). A data migration or manual database edit broke the foreign key relationship. The booking was hard-deleted but the payment was not cascaded.

Common situations: Stripe webhook arrives before the booking transaction commits (timing issue). Manual database cleanup that deleted bookings without cleaning payments. A failed booking flow that partially created a payment record. Database replication lag where the booking hasn't propagated to the read replica.

Related errors


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