immich-app/immich · error · BadRequestException

User is owner

Error message

User is owner

What it means

Raised by AlbumService.updateUser when attempting to change the role of the album's owner. The endpoint for editing a member's role rejects updates targeting the owner because ownership cannot be changed this way (an album has exactly one owner); the target user must be a non-owner shared member to have their role edited.

Solutions

  1. Skip the owner when bulk-updating member roles
  2. Only call updateUser for members with Editor/Viewer roles
  3. Filter the member list client-side: update only entries whose user id differs from album.ownerId

Example fix

// before
for (const m of album.albumUsers) await api.updateAlbumUser(id, m.userId, { role: 'Viewer' });
// after
for (const m of album.albumUsers.filter(m => m.userId !== album.ownerId)) {
  await api.updateAlbumUser(id, m.userId, { role: 'Viewer' });
}
Defensive patterns

Strategy: validation

Validate before calling

const album = await api.getAlbumInfo(albumId);
if (album.ownerId === userId) throw new Error('Cannot change the owner role');

Type guard

function isNonOwnerMember(userId: string, album: { ownerId: string }): boolean {
  return userId !== album.ownerId;
}

Try / catch

try {
  await api.updateAlbumUser(albumId, userId, { role });
} catch (e) {
  if ((e as Error).message === 'User is owner') {
    return; // skip owner in bulk role updates
  }
  throw e;
}

Prevention

When it happens

Trigger: PUT /albums/:id/user/:userId with the owner's userId in the path, regardless of the role in the DTO.

Common situations: Clients iterating all album members (including the owner) and updating each, or UIs rendering the owner as an editable row.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

      throw new BadRequestException('Cannot remove the last album owner');
    }

    // non-admin can remove themselves
    if (auth.user.id !== userId) {
      await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [id] });
    }

    await this.albumUserRepository.delete({ albumId: id, userId });
  }

  async updateUser(auth: AuthDto, id: string, userId: string, dto: UpdateAlbumUserDto): Promise<void> {
    await this.requireAccess({ auth, permission: Permission.AlbumShare, ids: [id] });

    const album = await this.findOrFail(id, userId, { withAssets: false });
    const owner = album.albumUsers[0];

    if (owner.user.id === userId) {
      throw new BadRequestException('User is owner');
    }

    await this.albumUserRepository.update({ albumId: id, userId }, { role: dto.role });
  }

  private findOrFail(id: string, authUserId: string, options: AlbumInfoOptions) {
    return findOrFail(() => this.albumRepository.getById(id, options, authUserId), 'Album');
  }
}

View on GitHub (pinned to e55ac299a4)