immich-app/immich · error · BadRequestException

Cannot add another owner

Error message

Cannot add another owner

What it means

Raised by AlbumService.addUsers when a requested albumUser entry has role AlbumUserRole.Owner. Albums are single-owner in Immich, so any attempt to add an additional owner (or re-assign ownership via the invite/share endpoint) is rejected with a 400 before the AlbumInvite event is emitted or any album user row is created. The offending input is the role field of an entry in the albumUsers array of AddUsersDto.

Solutions

  1. Send only 'Editor' or 'Viewer' roles in albumUsers
  2. Filter out owner-role entries and the existing owner from the payload before calling addUsers
  3. To transfer ownership, use the dedicated partner/ownership flow if available — there is no add-owner API

Example fix

// before
await api.addAlbumUsers(id, [{ userId, role: 'Owner' }]);
// after
await api.addAlbumUsers(id, [{ userId, role: 'Editor' }]);
Defensive patterns

Strategy: validation

Validate before calling

const safe = albumUsers.filter(u => u.role !== 'Owner');
if (safe.length !== albumUsers.length) throw new Error('Owner role is not allowed for album members');

Type guard

function isAssignableRole(r: string): r is 'Editor' | 'Viewer' {
  return r === 'Editor' || r === 'Viewer';
}

Try / catch

try {
  await api.addAlbumUsers(id, albumUsers);
} catch (e) {
  if ((e as Error).message === 'Cannot add another owner') {
    return api.addAlbumUsers(id, albumUsers.filter(u => u.role !== 'Owner'));
  }
  throw e;
}

Prevention

When it happens

Trigger: PUT /albums/:id/users (or addUsers) with an entry like { userId, role: 'Owner' } in the albumUsers array.

Common situations: Clients echoing back the full member list (including the owner entry) from album info instead of sending only new members, or UIs that expose an owner role option.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at server/src/services/album.service.ts:294

      await this.eventRepository.emit('AlbumUpdate', {
        id,
        userIds: album.albumUsers.map(({ user }) => user.id),
        recipientIds: [],
      });
    }

    return results;
  }

  async addUsers(auth: AuthDto, id: string, { albumUsers }: AddUsersDto): Promise<AlbumResponseDto> {
    await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [id] });

    const album = await this.findOrFail(id, auth.user.id, { withAssets: false });

    for (const { userId, role } of albumUsers) {
      if (role === AlbumUserRole.Owner) {
        throw new BadRequestException('Cannot add another owner');
      }

      const exists = album.albumUsers.some(({ user: { id } }) => id === userId);
      if (exists) {
        continue;
      }

      const user = await this.userRepository.get(userId, {});
      if (!user) {
        this.logger.debug('Adding user to album failed: user not found');
        throw new BadRequestException('Invalid user');
      }

      await this.albumUserRepository.create({ userId, albumId: id, role });
      await this.eventRepository.emit('AlbumInvite', { id, userId, senderName: auth.user.name });
    }

    return mapAlbum(await this.findOrFail(id, auth.user.id, { withAssets: true }));

View on GitHub (pinned to e55ac299a4)