{"record":{"id":"0b106722f539a55a","repo":"immich-app/immich","slug":"cannot-delete-your-own-account","errorCode":null,"errorMessage":"Cannot delete your own account","messagePattern":"Cannot delete your own account","errorType":"exception","errorClass":"ForbiddenException","httpStatus":403,"severity":"error","filePath":"server/src/services/user-admin.service.ts","lineNumber":103,"sourceCode":"\n    if (dto.pinCode) {\n      dto.pinCode = await this.cryptoRepository.hashBcrypt(dto.pinCode, SALT_ROUNDS);\n    }\n\n    if (dto.storageLabel === '') {\n      dto.storageLabel = null;\n    }\n\n    const updatedUser = await this.userRepository.update(id, { ...dto, updatedAt: new Date() });\n\n    return mapUserAdmin(updatedUser);\n  }\n\n  async delete(auth: AuthDto, id: string, dto: UserAdminDeleteDto): Promise<UserAdminResponseDto> {\n    const { force } = dto;\n    await this.findOrFail(id, {});\n    if (auth.user.id === id) {\n      throw new ForbiddenException('Cannot delete your own account');\n    }\n\n    await this.albumRepository.softDeleteAll(id);\n\n    const status = force ? UserStatus.Removing : UserStatus.Deleted;\n    const user = await this.userRepository.update(id, { status, deletedAt: new Date() });\n\n    await this.eventRepository.emit('UserTrash', user);\n\n    if (force) {\n      await this.jobRepository.queue({ name: JobName.UserDelete, data: { id: user.id, force } });\n    }\n\n    return mapUserAdmin(user);\n  }\n\n  async restore(auth: AuthDto, id: string): Promise<UserAdminResponseDto> {\n    await this.findOrFail(id, { withDeleted: true });","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/user-admin.service.ts#L85-L121","documentation":"A ForbiddenException (HTTP 403) thrown by UserAdminService.delete when the authenticated admin's user id equals the target user id being deleted. Immich blocks self-deletion to prevent an admin from locking themselves out or orphaning the only admin account. The check runs after findOrFail but before any soft-delete side effects, so no state mutates.","triggerScenarios":"Calling DELETE /admin/users/:id where the :id path parameter matches the authenticated user's id (auth.user.id). Common with admin scripts that iterate over all user ids including the caller's own, or a UI that lists the current admin in the delete-eligible list.","commonSituations":"Bulk admin cleanup scripts that fetch all users and delete each one; frontend admin panels that do not filter the current user out of the delete list; automated test teardown that reuses the admin token for a self-delete call.","solutions":["Filter the calling user's id out of any bulk delete loop before issuing DELETE /admin/users/:id requests.","In the admin UI, disable or hide the delete action for the row whose id equals the logged-in admin's id.","If you genuinely need the account gone, have a different admin delete it, or transfer admin rights to another account first.","Add a client-side guard: if (auth.user.id === targetId) skip the delete call and surface a message."],"exampleFix":"// before\nfor (const id of userIds) {\n  await adminApi.deleteUser(id); // throws 403 when id === me\n}\n\n// after\nfor (const id of userIds) {\n  if (id === me.id) continue;\n  await adminApi.deleteUser(id);\n}","handlingStrategy":"validation","validationCode":"function canDeleteUser(authUser, targetId) {\n  if (authUser.id === targetId) {\n    return { ok: false, reason: 'Cannot delete your own account' };\n  }\n  return { ok: true };\n}\n// before delete:\nconst check = canDeleteUser(auth, id);\nif (!check.ok) { showError(check.reason); return; }","typeGuard":"const isSelfDelete = (authUserId: string, targetId: string) => authUserId === targetId;","tryCatchPattern":"try {\n  await adminApi.deleteUser(id);\n} catch (e) {\n  if (e.status === 403 && e.message === 'Cannot delete your own account') {\n    // skip self in bulk loop\n    continue;\n  }\n  throw e;\n}","preventionTips":["Always exclude auth.user.id from bulk delete collections before iterating.","In admin UIs, render the delete action disabled for the current admin's row.","Add a pre-flight check in shared admin tooling that fails fast on self-delete."],"tags":["authorization","user-admin","self-deletion-guard","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}