calcom/cal.diy · error · BadRequestException
Multiple SelecteCalendars found. Skipping deletion
Error message
Multiple SelecteCalendars found. Skipping deletion
What it means
BadRequestException (HTTP 400) thrown when the repository reports MULTIPLE_SELECTED_CALENDARS_FOUND during deletion. removeUserSelectedCalendar expects to remove exactly one row; finding two or more rows for the same (userId, integration, externalId) is an inconsistent state, so the service refuses to delete ambiguously rather than guessing which row.
Solutions
- Investigate and deduplicate the rows in the database (keep one, delete the rest) so deletion is unambiguous.
- Add a unique constraint on (userId, integration, externalId) if missing, to prevent recurrence.
- Temporarily delete by id or with a bulk delete to clear the duplicates, then retry the normal endpoint.
Example fix
// before — normal delete fails on duplicates
await api.delete(`/selected-calendars/${integration}/${externalId}`); // 400
// after — dedupe via admin/DB, then retry
// SQL: DELETE FROM "SelectedCalendar" WHERE id IN (SELECT id FROM ... GROUP BY ... HAVING COUNT(*)>1);
await api.delete(`/selected-calendars/${integration}/${externalId}`); Defensive patterns
Strategy: validation
Validate before calling
async function assertSingleCalendarRow(userId: number, integration: string, externalId: string) {
const rows = await api.get('/selected-calendars', { params: { userId, integration, externalId } });
if (rows.length > 1) throw new Error(`Duplicate rows (${rows.length}); dedupe before deleting`);
} Type guard
const hasUniqueCalendars = (rows: { integration: string; externalId: string }[]): boolean => {
const seen = new Set<string>();
return rows.every(r => { const k = `${r.integration}:${r.externalId}`; if (seen.has(k)) return false; seen.add(k); return true; });
}; Try / catch
try { await api.delete(`/selected-calendars/${integration}/${externalId}`); }
catch (e) { if (/multiple selectedcalendars/i.test(e.message)) { /* run dedupe, then retry */ } else throw e; } Prevention
- Add a unique constraint on (userId, integration, externalId) to prevent duplicate rows.
- Periodically audit for duplicate SelectedCalendar rows.
- Use upsert (not insert) when connecting calendars.
When it happens
Trigger: DELETE /v2/selected-calendars when the database contains duplicate SelectedCalendar rows for the same user/integration/externalId — typically from a past bug, a missing unique constraint, or a non-atomic upsert that inserted twice.
Common situations: Legacy data created before a unique constraint was added; concurrent connect flows racing to insert the same calendar; a manual DB import that duplicated rows.
Related errors
- An unexpected error occurred while deleting the selected…
- Booking with uid not found
- Could not set as default conferencing app
- ${error.message}
- Event type with uid not found
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/cdb7122d4d06fe72.
Report an issue: GitHub.
Appendix: source
Thrown at apps/api/v2/src/modules/selected-calendars/services/selected-calendars.service.ts:60
) {
const { integration, externalId, credentialId } = selectedCalendar;
await this.calendarsService.checkCalendarCredentials(Number(credentialId), user.id);
try {
const removedCalendarEntry = await this.selectedCalendarsRepository.removeUserSelectedCalendar(
user.id,
integration,
externalId,
undefined
);
await this.calendarsCacheService.deleteConnectedAndDestinationCalendarsCache(user.id);
return removedCalendarEntry;
} catch (error) {
if (error instanceof Error) {
if (error.message === NO_SELECTED_CALENDAR_FOUND) {
throw new NotFoundException(NO_SELECTED_CALENDAR_FOUND);
} else if (error.message === MULTIPLE_SELECTED_CALENDARS_FOUND) {
throw new BadRequestException(MULTIPLE_SELECTED_CALENDARS_FOUND);
} else {
throw new InternalServerErrorException(error.message);
}
}
throw new InternalServerErrorException(
"An unexpected error occurred while deleting the selected calendar"
);
}
}
}
View on GitHub (pinned to 176037d0af)