immich-app/immich · warning · BadRequestException

Cannot request to join your own cluster group

Error message

Cannot request to join your own cluster group

What it means

When creating a cluster-group join request, the requested userId is compared against the authenticated user's id. If they match, the caller is trying to request membership in a group they already belong to, which is nonsensical, so a BadRequestException is thrown. The target user must always be someone else.

Solutions

  1. Pass the userId of a different user (the person who wants to join), not your own.
  2. In the client, disable/hide the 'request to join' action for the caller's own group.
  3. If the user should switch groups, have them use the leave/merge flow rather than a self join-request.

Example fix

// before
await api.createClusterGroupRequest(groupId, { userId: auth.user.id }); // self-request → error
// after
await api.createClusterGroupRequest(groupId, { userId: otherUserId });
Defensive patterns

Strategy: validation

Validate before calling

// Never send the caller's own userId in a join request
if (requestBody.userId === currentUserId) {
  throw new Error('Cannot request to join your own cluster group');
}

Prevention

When it happens

Trigger: POSTing a cluster group join request where the DTO userId equals auth.user.id — i.e. an admin invites themselves into their own cluster group.

Common situations: UI bug sending the current user's id as the invitee; scripting the join-request API with your own userId instead of another member's; testing with a single account.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/0e6400fad9466ada. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/cluster-group.service.ts:43

    return requests.map((request) => mapClusterGroupRequest(request));
  }

  async getUsers(auth: AuthDto, clusterGroupId: string): Promise<UserResponseDto[]> {
    await this.requireAccess({ auth, permission: Permission.ClusterGroupRead, ids: [clusterGroupId] });

    const users = await this.clusterGroupRepository.getUsers({ clusterGroupId, userId: auth.user.id });
    return users.map((user) => mapUser(user));
  }

  async createRequest(
    auth: AuthDto,
    clusterGroupId: string,
    { userId }: ClusterGroupRequestCreateDto,
  ): Promise<MaybeDuplicate<ClusterGroupRequestResponseDto>> {
    await this.requireAccess({ auth, permission: Permission.ClusterGroupRequestCreate, ids: [clusterGroupId] });

    if (userId === auth.user.id) {
      throw new BadRequestException('Cannot request to join your own cluster group');
    }

    await findOrFail(() => this.userRepository.get(userId, {}), 'User');

    const request = await findOrFail(
      () => this.clusterGroupRepository.createRequest({ clusterGroupId, userId }),
      'Request',
    );

    if (request.isInserted) {
      await this.eventRepository.emit('ClusterGroupRequest', { clusterGroupId, userId, senderName: auth.user.name });
    }

    return { duplicate: !request.isInserted, value: mapClusterGroupRequest(request) };
  }

  async acceptRequest(auth: AuthDto, id: string): Promise<void> {
    await this.requireAccess({ auth, permission: Permission.ClusterGroupRequestRead, ids: [id] });

View on GitHub (pinned to e55ac299a4)