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
- 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.
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
- 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.
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
- Google Meet is already connected for this team.
- Booking redirect infinite not allowed.
- Google Meet is already connected for this user.
- User with id=${userId} has already authorized client with id
- No user id found in request params.
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/a8d5765462e7ddc9.
Report an issue: GitHub.