calcom/cal.diy · error · ConflictException

Ooo entry already exists.

Error message

Ooo entry already exists.

What it means

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.

Source

Thrown at apps/api/v2/src/modules/ooo/services/ooo.service.ts:86

      const existingOooRedirect = await this.oooRepository.findExistingOooRedirect(
        userId,
        start,
        end,
        toUserId
      );

      if (existingOooRedirect) {
        throw new BadRequestException("Booking redirect infinite not allowed.");
      }
    }
  }

  async checkDuplicateOOOEntry(userId: number, start?: Date, end?: Date) {
    if (start && end) {
      const duplicateEntry = await this.oooRepository.getOooByUserIdAndTime(userId, start, end);

      if (duplicateEntry) {
        throw new ConflictException("Ooo entry already exists.");
      }
    }
  }

  checkRedirectToSelf(userId: number, toUserId?: number) {
    if (toUserId && toUserId === userId) {
      throw new BadRequestException("Cannot redirect to self.");
    }
  }

  async checkIsValidOOO(userId: number, ooo: CreateOutOfOfficeEntryDto | UpdateOutOfOfficeEntryDto) {
    this.isStartBeforeEnd(ooo.start, ooo.end);
    await this.checkExistingOooRedirect(userId, ooo.start, ooo.end, ooo.toUserId);
    await this.checkDuplicateOOOEntry(userId, ooo.start, ooo.end);
    await this.checkRedirectToSelf(userId, ooo.toUserId);
    await this.checkUserEligibleForRedirect(userId, ooo.toUserId);
  }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Add idempotency: disable the submit button after first click and/or send an Idempotency-Key header so retries are deduped upstream.
  2. On a 409 with this message, treat it as success if the caller intended the same window — fetch and return the existing entry.
  3. For batch/sync jobs, check getOooByUserIdAndTime before inserting, or catch the 409 and skip.

Example fix

// before — fire-and-forget submit
button.onclick = () => api.post('/ooo', body);
// after — guard against double submit and treat 409 as ok
let pending = false;
button.onclick = async () => {
  if (pending) return;
  pending = true;
  try {
    await api.post('/ooo', body, { headers: { 'Idempotency-Key': key } });
  } catch (e) {
    if (e.status !== 409) throw e;
  } finally { pending = false; }
};
Defensive patterns

Strategy: try-catch

Validate before calling

async function createOooIdempotent(body: { userId: number; start: string; end: string }) {
  const dup = await api.get('/ooo', { params: { userId: body.userId, start: body.start, end: body.end } });
  if (dup.length) return dup[0];
  return api.post('/ooo', body, { headers: { 'Idempotency-Key': `${body.userId}:${body.start}:${body.end}` } });
}

Try / catch

try { await api.post('/ooo', body); }
catch (e) { if (e.status === 409 && /already exists/i.test(e.message)) { /* treat as success */ } else throw e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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