{"record":{"id":"4d2e3699e863c1ce","repo":"immich-app/immich","slug":"admin-status-can-only-be-changed-by-another-admin","errorCode":null,"errorMessage":"Admin status can only be changed by another admin","messagePattern":"Admin status can only be changed by another admin","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/user-admin.service.ts","lineNumber":60,"sourceCode":"    await this.eventRepository.emit('UserSignup', {\n      notify: !!notify,\n      id: user.id,\n      password: userDto.password,\n    });\n\n    return mapUserAdmin(user);\n  }\n\n  async get(auth: AuthDto, id: string): Promise<UserAdminResponseDto> {\n    const user = await this.findOrFail(id, { withDeleted: true });\n    return mapUserAdmin(user);\n  }\n\n  async update(auth: AuthDto, id: string, dto: UserAdminUpdateDto): Promise<UserAdminResponseDto> {\n    const user = await this.findOrFail(id, {});\n\n    if (dto.isAdmin !== undefined && dto.isAdmin !== auth.user.isAdmin && auth.user.id === id) {\n      throw new BadRequestException('Admin status can only be changed by another admin');\n    }\n\n    if (dto.quotaSizeInBytes && user.quotaSizeInBytes !== dto.quotaSizeInBytes) {\n      await this.userRepository.syncUsage(id);\n    }\n\n    if (dto.email) {\n      const duplicate = await this.userRepository.getByEmail(dto.email);\n      if (duplicate && duplicate.id !== id) {\n        this.logger.debug('Email already in use by another account');\n        throw new BadRequestException('Email is not available');\n      }\n    }\n\n    if (dto.storageLabel) {\n      const duplicate = await this.userRepository.getByStorageLabel(dto.storageLabel);\n      if (duplicate && duplicate.id !== id) {\n        throw new BadRequestException('Storage label already in use by another account');","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/user-admin.service.ts#L42-L78","documentation":"Thrown by UserAdminService.update when a user attempts to change their own isAdmin flag. Only another admin may grant or revoke admin status, preventing an admin from irreversibly altering their own privileges (e.g. demoting themselves). It is a 400 BadRequest for a self-modification attempt.","triggerScenarios":"PUT/PATCH /api/admin/users/:id where id === auth.user.id and dto.isAdmin is defined and differs from the caller's current isAdmin value.","commonSituations":"Admin editing their own profile in the UI and accidentally toggling the admin switch; bulk-update scripts that include isAdmin on every user including the caller; self-service profile forms submitting the full user object.","solutions":["Have a different admin perform the isAdmin change","Remove the isAdmin field from self-update payloads","Split the profile form so privilege fields are only sent when editing other users"],"exampleFix":"// before\nawait adminApi.updateUser(myId, { name, isAdmin: false });\n// after\nawait adminApi.updateUser(myId, { name }); // omit isAdmin for self-updates","handlingStrategy":"validation","validationCode":"if (dto.isAdmin !== undefined && id === auth.user.id && dto.isAdmin !== auth.user.isAdmin) {\n  throw new Error('cannot change your own admin status');\n}","typeGuard":null,"tryCatchPattern":"try { await adminApi.updateUser(id, dto); } catch (e) {\n  if (e.response?.status === 400 && /Admin status/.test(e.response?.data?.message ?? '')) {\n    const { isAdmin, ...rest } = dto;\n    return adminApi.updateUser(id, rest); // retry without isAdmin\n  }\n  throw e;\n}","preventionTips":["Strip isAdmin from self-update payloads","Route admin-flag changes through a separate endpoint/flow requiring another admin","In bulk updates, skip or sanitize the caller's own record"],"tags":["permission","self-modification"],"backgroundTag":"permission-denied","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}