{"record":{"id":"ddfcf8591686d230","repo":"calcom/cal.diy","slug":"could-not-create-recurring-booking","errorCode":null,"errorMessage":"Could not create recurring booking.","messagePattern":"Could not create recurring booking\\.","errorType":"http","errorClass":"InternalServerErrorException","httpStatus":500,"severity":"error","filePath":"apps/api/v2/src/platform/bookings/2024-04-15/controllers/bookings.controller.ts","lineNumber":352,"sourceCode":"          hostname: bookingRequest.headers?.host || \"\",\n          platformClientId: bookingRequest.platformClientId,\n          platformRescheduleUrl: bookingRequest.platformRescheduleUrl,\n          platformCancelUrl: bookingRequest.platformCancelUrl,\n          platformBookingUrl: bookingRequest.platformBookingUrl,\n          platformBookingLocation: bookingRequest.platformBookingLocation,\n          noEmail: bookingRequest.body.noEmail,\n        },\n        creationSource: \"API_V2\",\n      });\n\n      return {\n        status: SUCCESS_STATUS,\n        data: createdBookings,\n      };\n    } catch (err) {\n      this.handleBookingErrors(err, \"recurring\");\n    }\n    throw new InternalServerErrorException(\"Could not create recurring booking.\");\n  }\n\n  private async getOwner(req: Request): Promise<{ id: number; uuid: string } | null> {\n    try {\n      const bearerToken = req.get(\"Authorization\")?.replace(\"Bearer \", \"\");\n      if (!bearerToken) {\n        return null;\n      }\n\n      let ownerId: number | null = null;\n\n      if (isApiKey(bearerToken, this.config.get<string>(\"api.apiKeyPrefix\") ?? \"cal_\")) {\n        const strippedApiKey = stripApiKey(bearerToken, this.config.get<string>(\"api.keyPrefix\"));\n        const apiKeyHash = sha256Hash(strippedApiKey);\n        const keyData = await this.apiKeyRepository.getApiKeyFromHash(apiKeyHash);\n        ownerId = keyData?.userId ?? null;\n      } else {\n        // Access Token","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/bookings/2024-04-15/controllers/bookings.controller.ts#L334-L370","documentation":"Thrown as a 500 InternalServerError after the recurring-booking try/catch's handleBookingErrors call. handleBookingErrors is typed to return void (not 'never'), so TypeScript control-flow analysis sees the possibility of falling through past the catch block. This throw is the compiler-required safety net; in practice handleBookingErrors always throws, so this message indicates either an unhandled error shape or a logic bug in handleBookingErrors itself.","triggerScenarios":"POST /v2/bookings/recurring where the recurringBookingService.createBooking call throws an error that handleBookingErrors processes. The message surfaces only if handleBookingErrors returns without throwing (e.g., the error object is of an unexpected type that doesn't match HttpError or Error instanceof checks, or a future refactor breaks the always-throw contract).","commonSituations":"A non-Error value is thrown inside the recurring booking service (e.g., a bare string, a rejected promise with a non-Error rejection reason, or a Prisma internal error that doesn't extend Error). Also seen after refactoring handleBookingErrors where a new code path returns instead of throws.","solutions":["Inspect the server logs for the original error that handleBookingErrors swallowed — the logger.error calls in dependent services capture the root cause.","Verify the thrown value is a proper Error instance. If a service throws a plain object or string, wrap it: throw new Error(...).","If you control the recurring booking service, ensure all catch paths throw a typed Error, HttpError, or HttpException so handleBookingErrors can route correctly.","Consider changing handleBookingErrors return type to 'never' so TypeScript guarantees it always throws, eliminating this dead code path."],"exampleFix":"// before\nprivate handleBookingErrors(err: Error | HttpError | unknown, type?: ...): void {\n  // ...\n  throw new InternalServerErrorException(errMsg);\n}\n\n// after — tell TypeScript this always throws\nprivate handleBookingErrors(err: Error | HttpError | unknown, type?: ...): never {\n  // ...\n  throw new InternalServerErrorException(errMsg);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Client-side: catch 500 and inspect the body for context\ntry {\n  const res = await fetch('/v2/bookings/recurring', { method: 'POST', body: JSON.stringify(payload) });\n  if (!res.ok) {\n    const body = await res.json();\n    // body.message may contain the underlying error if handleBookingErrors forwarded it\n    throw new Error(body.message || 'Recurring booking failed');\n  }\n} catch (err) {\n  // Retry with corrected payload or surface to user\n  console.error('Recurring booking creation failed:', err);\n}","preventionTips":["Validate the recurring booking payload client-side before sending — ensure all required fields (eventTypeId, start, attendee info) are present.","Log the full response body on 500 errors — the underlying cause is often forwarded by handleBookingErrors.","If this error fires consistently, reproduce with server-side verbose logging to capture the original error that escaped handleBookingErrors."],"tags":["nestjs","booking","recurring","unreachable-code","error-handling"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}