{"record":{"id":"a8d5765462e7ddc9","repo":"calcom/cal.diy","slug":"ooo-entry-already-exists","errorCode":null,"errorMessage":"Ooo entry already exists.","messagePattern":"Ooo entry already exists\\.","errorType":"http","errorClass":"ConflictException","httpStatus":409,"severity":"error","filePath":"apps/api/v2/src/modules/ooo/services/ooo.service.ts","lineNumber":86,"sourceCode":"      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    }\n  }\n\n  async checkIsValidOOO(userId: number, ooo: CreateOutOfOfficeEntryDto | UpdateOutOfOfficeEntryDto) {\n    this.isStartBeforeEnd(ooo.start, ooo.end);\n    await this.checkExistingOooRedirect(userId, ooo.start, ooo.end, ooo.toUserId);\n    await this.checkDuplicateOOOEntry(userId, ooo.start, ooo.end);\n    await this.checkRedirectToSelf(userId, ooo.toUserId);\n    await this.checkUserEligibleForRedirect(userId, ooo.toUserId);\n  }\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/ooo/services/ooo.service.ts#L68-L104","documentation":"ConflictException (HTTP 409) thrown by checkDuplicateOOOEntry() when getOooByUserIdAndTime() finds an OutOfOfficeEntry with the exact same userId, start, AND end. The duplicate check matches all three fields precisely — same user, same start instant, same end instant — so near-identical but not byte-identical windows do not trigger it.","triggerScenarios":"POST /v2/ooo retried with the identical body (e.g. user double-clicks submit, or an automatic retry after a network blip); a sync script re-creating OOO entries that already exist for the same exact window.","commonSituations":"Duplicate-submit protection missing on the client; idempotency-key not used so retries re-insert; migrations/seeders run twice; UI 'save' fired twice during slow network.","solutions":["Add idempotency: disable the submit button after first click and/or send an Idempotency-Key header so retries are deduped upstream.","On a 409 with this message, treat it as success if the caller intended the same window — fetch and return the existing entry.","For batch/sync jobs, check getOooByUserIdAndTime before inserting, or catch the 409 and skip."],"exampleFix":"// before — fire-and-forget submit\nbutton.onclick = () => api.post('/ooo', body);\n// after — guard against double submit and treat 409 as ok\nlet pending = false;\nbutton.onclick = async () => {\n  if (pending) return;\n  pending = true;\n  try {\n    await api.post('/ooo', body, { headers: { 'Idempotency-Key': key } });\n  } catch (e) {\n    if (e.status !== 409) throw e;\n  } finally { pending = false; }\n};","handlingStrategy":"try-catch","validationCode":"async function createOooIdempotent(body: { userId: number; start: string; end: string }) {\n  const dup = await api.get('/ooo', { params: { userId: body.userId, start: body.start, end: body.end } });\n  if (dup.length) return dup[0];\n  return api.post('/ooo', body, { headers: { 'Idempotency-Key': `${body.userId}:${body.start}:${body.end}` } });\n}","typeGuard":null,"tryCatchPattern":"try { await api.post('/ooo', body); }\ncatch (e) { if (e.status === 409 && /already exists/i.test(e.message)) { /* treat as success */ } else throw e; }","preventionTips":["Disable the submit button after first click and send an Idempotency-Key.","Treat 409 'already exists' as success when the intent is the same window.","In batch jobs, de-duplicate by (userId, start, end) before inserting."],"tags":["conflict","duplicate","ooo","nestjs","idempotency"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}