{"record":{"id":"ff2787e68a186043","repo":"calcom/cal.diy","slug":"booking-redirect-infinite-not-allowed","errorCode":null,"errorMessage":"Booking redirect infinite not allowed.","messagePattern":"Booking redirect infinite not allowed\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/ooo/services/ooo.service.ts","lineNumber":76,"sourceCode":"    if (toUserId) {\n      const user = await this.usersRepository.findUserOOORedirectEligible(userId, toUserId);\n      if (!user) {\n        throw new BadRequestException(\"Cannot redirect to this user.\");\n      }\n    }\n  }\n\n  async checkExistingOooRedirect(userId: number, start?: Date, end?: Date, toUserId?: number) {\n    if (start && end) {\n      const existingOooRedirect = await this.oooRepository.findExistingOooRedirect(\n        userId,\n        start,\n        end,\n        toUserId\n      );\n\n      if (existingOooRedirect) {\n        throw new BadRequestException(\"Booking redirect infinite not allowed.\");\n      }\n    }\n  }\n\n  async checkDuplicateOOOEntry(userId: number, start?: Date, end?: Date) {\n    if (start && end) {\n      const duplicateEntry = await this.oooRepository.getOooByUserIdAndTime(userId, start, end);\n\n      if (duplicateEntry) {\n        throw new ConflictException(\"Ooo entry already exists.\");\n      }\n    }\n  }\n\n  checkRedirectToSelf(userId: number, toUserId?: number) {\n    if (toUserId && toUserId === userId) {\n      throw new BadRequestException(\"Cannot redirect to self.\");\n    }","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/ooo/services/ooo.service.ts#L58-L94","documentation":"Thrown by checkExistingOooRedirect() when an existing OutOfOfficeEntry is found for the requested time window that would create a redirect cycle. The repository query looks for any entry whose toUserId is the current user and whose time range overlaps or sits inside the new window — i.e. someone is already redirecting TO this user, so this user redirecting onward (or back) risks an infinite booking-redirect chain.","triggerScenarios":"User A already has an OOO redirect pointing to User B for a window overlapping the request; now User B (or A) tries to set up a redirect in that same window. Also fires when the target user is already a redirect source in the window (toUserId chain).","commonSituations":"Two teammates going on vacation and pointing at each other; chaining redirects A→B→C where C redirects back to A; overlapping OOO windows that were created independently and now collide on edit.","solutions":["Before creating a redirect, query existing redirects for the window and choose a target who is not already redirecting in that period.","Shorten or move the window so it does not overlap an existing redirect that points back into the chain.","Clear the conflicting redirect (delete the other OOO entry) before setting up the new one, if business rules allow."],"exampleFix":"// before — pick any teammate\nconst target = teammates[0].userId;\nawait api.post('/ooo', { start, end, toUserId: target });\n// after — exclude targets already in a redirect cycle for this window\nconst busy = await api.get('/ooo/redirects', { start, end });\nconst free = teammates.filter(t => !busy.includes(t.userId));\nawait api.post('/ooo', { start, end, toUserId: free[0].userId });","handlingStrategy":"try-catch","validationCode":"async function findCycleFreeTarget(window: { start: string; end: string }, candidates: number[]): Promise<number | undefined> {\n  const existing = await api.get('/ooo', { params: window }); // entries that already redirect into this user\n  const blocked = new Set(existing.map((e: { toUserId: number; userId: number }) => e.userId));\n  return candidates.find(id => !blocked.has(id));\n}","typeGuard":null,"tryCatchPattern":"try { await api.post('/ooo', body); }\ncatch (e) {\n  if (/redirect infinite/i.test(e.message)) { /* pick a different toUserId or shrink window */ }\n  else throw e;\n}","preventionTips":["Before creating a redirect, query existing OOO redirects for the window and exclude targets already in a chain.","Avoid overlapping redirect windows between teammates who redirect to each other.","When editing, check that the new window does not now overlap a back-pointer."],"tags":["business-rule","ooo","redirect-loop","conflict","nestjs"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}