toeverything/AFFiNE · error · CannotDeleteOwnAccount

cannot_delete_own_account

cannot_delete_own_account

Error message

Cannot delete own account.

What it means

Thrown by the admin deleteUser mutation when the target id equals the authenticated user's own id. The admin user-deletion path is for managing other accounts; self-deletion must go through deleteAccount instead. This guard prevents an admin from locking themselves out by deleting their own account through the admin endpoint.

Source

Thrown at packages/backend/server/src/core/user/resolver.ts:383

        return sessionUser(result.value);
      } else {
        return {
          email: input.users[i].email,
          error: result.reason.message,
        };
      }
    });
  }

  @Mutation(() => DeleteAccount, {
    description: 'Delete a user account',
  })
  async deleteUser(
    @CurrentUser() user: CurrentUser,
    @Args('id') id: string
  ): Promise<DeleteAccount> {
    if (user.id === id) {
      throw new CannotDeleteOwnAccount();
    }
    await this.models.user.delete(id);
    return { success: true };
  }

  @Mutation(() => UserType, {
    description: 'Update an user',
  })
  async updateUser(
    @Args('id') id: string,
    @Args('input') input: ManageUserInput
  ): Promise<UserType> {
    const user = await this.db.user.findUnique({
      where: { id },
    });

    if (!user) {
      throw new UserNotFound();

View on GitHub (pinned to 26c515e050)

Solutions

  1. Use the deleteAccount mutation (not deleteUser) to delete your own account.
  2. In the admin UI, hide/disable the delete action for the currently logged-in admin's row.
  3. Guard the client call: skip deleteUser when targetId === session.userId.

Example fix

// before
await deleteUser({ id: session.userId });

// after
if (targetId === session.userId) {
  await deleteAccount();
} else {
  await deleteUser({ id: targetId });
}
Defensive patterns

Strategy: validation

Validate before calling

if (targetId === session.userId) { throw new Error('Use deleteAccount for self-deletion'); }

Type guard

function isSelfDelete(targetId: string, currentUserId: string): boolean {
  return targetId === currentUserId;
}

Try / catch

try {
  await deleteUser({ id: targetId });
} catch (e) {
  if (e?.code === 'cannot_delete_own_account') { await deleteAccount(); return; }
  throw e;
}

Prevention

When it happens

Trigger: An admin invokes deleteUser(id) where id === currentUser.id.

Common situations: Admin UI prefilled with the current user's id; admin attempting self-cleanup via the wrong mutation; client routing self-delete to the admin mutation by mistake.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/5313563918e22785. Report an issue: GitHub.