{"record":{"id":"511ff19fa754d2c9","repo":"calcom/cal.diy","slug":"booking-with-uid-uid-not-found","errorCode":null,"errorMessage":"Booking with uid ${uid} not found","messagePattern":"Booking with uid (.+?) not found","errorType":"exception","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts","lineNumber":321,"sourceCode":"              isInstalled: !!userCredentialIds.length || !!teams.length || app.isGlobal,\n              isSetupAlready,\n              ...(app.dependencies && { dependencyData }),\n            };\n          }\n        )\n    );\n    return apps[0];\n  }\n\n  async getUserPaymentInfo(uid: string) {\n    const rawPayment = await this.atomsRepository.getRawPayment(uid);\n    if (!rawPayment) throw new NotFoundException(`Payment with uid ${uid} not found`);\n    const { data, booking: _booking, ...restPayment } = rawPayment;\n    const payment = {\n      ...restPayment,\n      data: data as Record<string, unknown>,\n    };\n    if (!_booking) throw new NotFoundException(`Booking with uid ${uid} not found`);\n    const { startTime, endTime, eventType, ...restBooking } = _booking;\n    const booking = {\n      ...restBooking,\n      startTime: startTime.toString(),\n      endTime: endTime.toString(),\n    };\n    if (!eventType) throw new NotFoundException(`Event type with uid ${uid} not found`);\n    if (eventType.users.length === 0 && !eventType.team)\n      throw new NotFoundException(`No users found or no team present for event type with uid ${uid}`);\n    const [user] = eventType?.users.length\n      ? eventType.users\n      : [{ name: null, theme: null, hideBranding: null, username: null }];\n    const profile = {\n      name: eventType.team?.name || user?.name || null,\n      theme: (!eventType.team?.name && user?.theme) || null,\n      hideBranding: eventType.team?.hideBranding || user?.hideBranding || null,\n    };\n    return {","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/atoms/services/event-types-atom.service.ts#L303-L339","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Investigate the payment record in the database: check if bookingId is null or references a non-existent booking.","If this is a timing issue (webhook race), retry after a short delay to see if the booking becomes available.","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.","If the booking was legitimately deleted, remove the orphaned payment record or re-associate it."],"exampleFix":"// before: no retry on transient missing booking\nconst info = await api.get(`/v2/atoms/payment-info/${paymentUid}`);\n\n// after: retry with backoff for webhook race conditions\nconst fetchWithRetry = async (uid, retries = 3) => {\n  for (let i = 0; i < retries; i++) {\n    try {\n      return await api.get(`/v2/atoms/payment-info/${uid}`);\n    } catch (e) {\n      if (e.statusCode === 404 && i < retries - 1) {\n        await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n        continue;\n      }\n      throw e;\n    }\n  }\n};","handlingStrategy":"retry","validationCode":"// For webhook-driven flows, verify the booking exists before querying payment info\nconst verifyBookingExists = async (api: ApiClient, paymentUid: string): Promise<void> => {\n  const payment = await api.get(`/v2/payments/${paymentUid}`);\n  if (!payment?.bookingId) {\n    throw new Error('Payment exists but has no associated booking. Possible data integrity issue.');\n  }\n};","typeGuard":null,"tryCatchPattern":"// Retry for webhook race conditions where booking hasn't committed yet\nconst getPaymentInfoWithRetry = async (uid: string, maxRetries = 3): Promise<any> => {\n  for (let i = 0; i < maxRetries; i++) {\n    try {\n      return await api.get(`/v2/atoms/payment-info/${uid}`);\n    } catch (err: any) {\n      const msg = err?.response?.data?.message ?? '';\n      if (err?.response?.status === 404 && msg.includes('Booking') && i < maxRetries - 1) {\n        await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n        continue;\n      }\n      throw err;\n    }\n  }\n};","preventionTips":["In webhook handlers, delay payment-info queries until after the booking transaction commits.","Run periodic data integrity checks: SELECT p.uid FROM payment p LEFT JOIN booking b ON p.\"bookingId\" = b.id WHERE p.\"bookingId\" IS NOT NULL AND b.id IS NULL.","When processing Stripe webballs, use idempotency keys to prevent duplicate payment records with missing bookings."],"tags":["not-found","payment","booking","data-integrity","atoms","nestjs","api-v2"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}