{"record":{"id":"0e6400fad9466ada","repo":"immich-app/immich","slug":"cannot-request-to-join-your-own-cluster-group","errorCode":null,"errorMessage":"Cannot request to join your own cluster group","messagePattern":"Cannot request to join your own cluster group","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/cluster-group.service.ts","lineNumber":43,"sourceCode":"    return requests.map((request) => mapClusterGroupRequest(request));\n  }\n\n  async getUsers(auth: AuthDto, clusterGroupId: string): Promise<UserResponseDto[]> {\n    await this.requireAccess({ auth, permission: Permission.ClusterGroupRead, ids: [clusterGroupId] });\n\n    const users = await this.clusterGroupRepository.getUsers({ clusterGroupId, userId: auth.user.id });\n    return users.map((user) => mapUser(user));\n  }\n\n  async createRequest(\n    auth: AuthDto,\n    clusterGroupId: string,\n    { userId }: ClusterGroupRequestCreateDto,\n  ): Promise<MaybeDuplicate<ClusterGroupRequestResponseDto>> {\n    await this.requireAccess({ auth, permission: Permission.ClusterGroupRequestCreate, ids: [clusterGroupId] });\n\n    if (userId === auth.user.id) {\n      throw new BadRequestException('Cannot request to join your own cluster group');\n    }\n\n    await findOrFail(() => this.userRepository.get(userId, {}), 'User');\n\n    const request = await findOrFail(\n      () => this.clusterGroupRepository.createRequest({ clusterGroupId, userId }),\n      'Request',\n    );\n\n    if (request.isInserted) {\n      await this.eventRepository.emit('ClusterGroupRequest', { clusterGroupId, userId, senderName: auth.user.name });\n    }\n\n    return { duplicate: !request.isInserted, value: mapClusterGroupRequest(request) };\n  }\n\n  async acceptRequest(auth: AuthDto, id: string): Promise<void> {\n    await this.requireAccess({ auth, permission: Permission.ClusterGroupRequestRead, ids: [id] });","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/immich-app/immich/blob/5666d57f15a66bd5518119c5d9f4d2b62f3a86c1/server/src/services/cluster-group.service.ts#L25-L61","documentation":"Immich's ClusterGroupService.createRequest (POST /cluster-groups/:id/requests, Permission.ClusterGroupRequestCreate) rejects a join request whose userId equals the authenticated user. A cluster group shares face/person clustering between users; requesting to join a group you are already part of is a no-op that would also spam a ClusterGroupRequest event to yourself, so it is blocked with BadRequestException (HTTP 400) before the user lookup and insert run.","triggerScenarios":"POST /cluster-groups/{clusterGroupId}/requests with body { \"userId\": \"<your own user id>\" } — i.e. the sender of the request and the invited user are the same person. Typically caused by a UI defaulting the user picker to the current user, or a script iterating a member list that accidentally includes the caller.","commonSituations":"Frontend invite forms that preselect the logged-in user. Test harnesses that seed one user and call the invite endpoint with that same user. Copy-pasted invitation code where auth.user.id is passed instead of the selected member's id.","solutions":["Send the id of the user you want to invite (dto.userId of a different user), not auth.user.id.","In the client, exclude the current user from the user picker / candidate list for the invite form.","Add a client-side guard: if (dto.userId === session.user.id) show a validation message instead of hitting the API."],"exampleFix":"// before — UI preselects the logged-in user\nawait api.post(`/cluster-groups/${groupId}/requests`, {\n  userId: session.user.id, // 400: Cannot request to join your own cluster group\n});\n\n// after — send the invited member's id and exclude self from pickers\nconst candidates = allUsers.filter((u) => u.id !== session.user.id);\nawait api.post(`/cluster-groups/${groupId}/requests`, {\n  userId: selectedCandidate.id,\n});","handlingStrategy":"validation","validationCode":"// Before calling the invite endpoint:\nif (dto.userId === auth.user.id) {\n  throw new Error('You cannot invite yourself to a cluster group');\n}\nawait createClusterGroupRequest(auth, clusterGroupId, dto); // POST /cluster-groups/{id}/requests","typeGuard":null,"tryCatchPattern":"try {\n  return await api.post(`/cluster-groups/${groupId}/requests`, { userId });\n} catch (error) {\n  if (isHttpError(error, 400, 'Cannot request to join your own cluster group')) {\n    // refresh the candidate list, exclude the session user, and reprompt\n    return showInviteForm(candidates.filter((u) => u.id !== session.user.id));\n  }\n  throw error;\n}","preventionTips":["Exclude the current user from cluster-group invite pickers and candidate lists.","In scripts, assert dto.userId !== auth.user.id before sending the request.","Remember the server also 404s on unknown userIds (findOrFail on userRepository.get), so validate the target user exists too."],"tags":["immich","cluster-group","self-invitation","business-rule","http-400","invitations"],"backgroundTag":"self-invitation-blocked","analyzedSha":"5666d57f15a66bd5518119c5d9f4d2b62f3a86c1","analyzedAt":"2026-08-21T18:08:19.313Z","contentChangedAt":"2026-08-21T18:08:19.313Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}