{"record":{"id":"89ffa23190c97bf4","repo":"calcom/cal.diy","slug":"user-already-has-an-event-type-with-this-slug","errorCode":null,"errorMessage":"User already has an event type with this slug.","messagePattern":"User already has an event type with this slug\\.","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/platform/event-types/event-types_2024_04_15/services/event-types.service.ts","lineNumber":56,"sourceCode":"  ): Promise<EventTypeOutput> {\n    await this.checkCanCreateEventType(user.id, body);\n    const eventTypeUser = await this.getUserToCreateEvent(user);\n    const { eventType } = await createEventType({\n      input: body,\n      ctx: {\n        user: eventTypeUser,\n        // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n        // @ts-ignore\n        prisma: this.dbWrite.prisma,\n      },\n    });\n    return eventType as EventTypeOutput;\n  }\n\n  async checkCanCreateEventType(userId: number, body: CreateEventTypeInput_2024_04_15) {\n    const existsWithSlug = await this.eventTypesRepository.getUserEventTypeBySlug(userId, body.slug);\n    if (existsWithSlug) {\n      throw new BadRequestException(\"User already has an event type with this slug.\");\n    }\n  }\n\n  async getUserToCreateEvent(user: UserWithProfile) {\n    const organizationId = this.usersService.getUserMainOrgId(user);\n    const isOrgAdmin = organizationId\n      ? await this.membershipsRepository.isUserOrganizationAdmin(user.id, organizationId)\n      : false;\n    const profileId = this.usersService.getUserMainProfile(user)?.id || null;\n    return {\n      id: user.id,\n      role: user.role,\n      organizationId: user.organizationId,\n      organization: { isOrgAdmin },\n      profile: { id: profileId },\n      metadata: user.metadata,\n      email: user.email,\n    };","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/platform/event-types/event-types_2024_04_15/services/event-types.service.ts#L38-L74","documentation":"Thrown by EventTypesService_2024_04_15.checkCanCreateEventType (POST /v2/event-types) when eventTypesRepository.getUserEventTypeBySlug returns a row — meaning the authenticated user already owns an event type with the requested slug. Slugs are unique per user, so the create is rejected before any write.","triggerScenarios":"POST /v2/event-types with a `slug` that duplicates an existing event type's slug for the same user; re-issuing a create after a partial success; slug collision after renaming another event type to the same slug.","commonSituations":"Retrying a create that actually succeeded (network blip) but with the same slug; generating slugs deterministically from a title that already exists; importing event types without slug deduplication; frontend not refreshing the list after a create.","solutions":["GET /v2/event-types and check the slug is unused before POSTing.","Generate a unique slug by appending a suffix (e.g. '-1', '-2') when the preferred slug is taken.","If the previous create may have succeeded, treat this 400 as a hint to refresh and switch to PATCH on the existing id.","Make slug generation idempotent by deriving it from a stable external id rather than the title."],"exampleFix":"// before\nawait api.post('/v2/event-types', { slug: 'thirty-min', length: 30, ... });\n// after\nconst existing = (await api.get('/v2/event-types')).data.find(e => e.slug === 'thirty-min');\nif (existing) throw new Error('slug taken');\nawait api.post('/v2/event-types', { slug: 'thirty-min', length: 30, ... });","handlingStrategy":"validation","validationCode":"// Check slug uniqueness before create\nconst mine = (await api.get('/v2/event-types')).data ?? [];\nif (mine.some((e) => e.slug === body.slug)) {\n  throw new Error(`slug ${body.slug} already used by this user`);\n}","typeGuard":"function isUniqueSlug(list: unknown, slug: string): boolean {\n  return Array.isArray(list) && !list.some((e) => typeof e === 'object' && e !== null && (e as any).slug === slug);\n}","tryCatchPattern":"try {\n  await api.post('/v2/event-types', body);\n} catch (e) {\n  if (e.response?.status === 400 && e.response?.data?.message?.includes('slug')) {\n    // append a suffix and retry, or switch to PATCH on the existing id\n  } else throw e;\n}","preventionTips":["Generate slugs deterministically and append a numeric suffix on collision.","After a create request times out, list event types before retrying to avoid duplicate slugs.","Keep a client-side set of used slugs per user and invalidate on every create/delete."],"tags":["nestjs","bad-request","event-types","uniqueness","rest-api"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}