calcom/cal.diy · error · BadRequestException

User already has an event type with this slug.

Error message

User already has an event type with this slug.

What it means

Thrown by EventTypesService_2024_06_14.checkCanCreateEventType (POST /v2/event-types, cal-api-version 2024-06-14) when getUserEventTypeBySlug returns a row — the user already owns an event type with the requested slug. Slug uniqueness is enforced per user before any write occurs. Identical message to the 2024_04_15 service; both versions share this contract.

Source

Thrown at apps/api/v2/src/platform/event-types/event-types_2024_06_14/services/event-types.service.ts:108

    const hasAccess = await this.eventTypeAccessService.userIsEventTypeAdminOrOwner(
      authUser,
      eventType as unknown as EventType
    );

    if (!hasAccess) {
      return null;
    }

    return {
      ownerId: eventType.userId ?? 0,
      ...eventType,
    };
  }

  async checkCanCreateEventType(userId: number, body: InputEventTransformed_2024_06_14) {
    const existsWithSlug = await this.eventTypesRepository.getUserEventTypeBySlug(userId, body.slug);
    if (existsWithSlug) {
      throw new BadRequestException("User already has an event type with this slug.");
    }
    await this.checkUserOwnsSchedule(userId, body.scheduleId);
  }

  checkHasUserAccessibleEmailBookingField(bookingFields: (SystemField | CustomField)[]) {
    const emailField = bookingFields.find((field) => field.type === "email" && field.name === "email");
    const isEmailFieldRequiredAndVisible = emailField?.required && !emailField?.hidden;
    if (!isEmailFieldRequiredAndVisible) {
      throw new BadRequestException(
        "checkIsEmailUserAccessible - Email booking field must be required and visible"
      );
    }
  }

  async getEventTypeByUsernameAndSlug(params: {
    username: string;
    eventTypeSlug: string;
    orgSlug?: string;

View on GitHub (pinned to 176037d0af)

Solutions

  1. GET /v2/event-types (cal-api-version 2024-06-14) and confirm the slug is unused before POSTing.
  2. Append a numeric suffix to the slug when the preferred one is taken.
  3. If retrying after a possible success, list event types and PATCH the existing one instead of re-creating.
  4. Derive slugs from a stable external id to make retries idempotent.

Example fix

// before
await api.post('/v2/event-types', { slug: 'intro-call', ... }, { headers: { 'cal-api-version': '2024-06-14' } });
// after
const mine = (await api.get('/v2/event-types', { headers: { 'cal-api-version': '2024-06-14' } })).data;
if (mine.some(e => e.slug === 'intro-call')) throw new Error('slug taken');
await api.post('/v2/event-types', { slug: 'intro-call', ... }, { headers: { 'cal-api-version': '2024-06-14' } });
Defensive patterns

Strategy: validation

Validate before calling

// Check slug uniqueness before create (2024_06_14)
const mine = (await api.get('/v2/event-types', { headers: { 'cal-api-version': '2024-06-14' } })).data ?? [];
if (mine.some((e) => e.slug === body.slug)) {
  throw new Error(`slug ${body.slug} already used by this user`);
}

Type guard

function isSlugAvailable(list: unknown, slug: string): boolean {
  return Array.isArray(list) && !list.some((e) => typeof e === 'object' && e !== null && (e as any).slug === slug);
}

Try / catch

try {
  await api.post('/v2/event-types', body, { headers: { 'cal-api-version': '2024-06-14' } });
} catch (e) {
  if (e.response?.status === 400 && e.response?.data?.message?.includes('slug')) {
    // append suffix or switch to PATCH on the existing id
  } else throw e;
}

Prevention

When it happens

Trigger: POST /v2/event-types with a slug duplicating an existing one for the same user; retrying a successful create with the same slug; slug collision after a rename; importing event types without dedup.

Common situations: Network-retry of a succeeded create; deterministic slug generation from a title that already exists; migrating event types between users without changing the slug; frontend not refreshing after create.

Related errors


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