toeverything/AFFiNE · error · CanNotRevokeYourself

can_not_revoke_yourself

can_not_revoke_yourself

Error message

You can not revoke your own permission.

What it means

Thrown by the `revokeMember` mutation when the target `userId` equals the caller's own id (`me.id`). The workspace service forbids self-revocation as a safety guard so an admin cannot accidentally strip their own access. Categorised under `can_not_revoke_yourself` (action_forbidden).

Source

Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:646

        inviteeId
      );
      status = invitation?.status;
    } else {
      const invitation = await this.models.workspaceUser.getById(inviteId);
      status = invitation?.status;
    }

    return { workspace, user: owner, invitee, status };
  }

  @Mutation(() => Boolean)
  async revokeMember(
    @CurrentUser() me: CurrentUser,
    @Args('workspaceId') workspaceId: string,
    @Args('userId') userId: string
  ) {
    if (userId === me.id) {
      throw new CanNotRevokeYourself();
    }

    const role = await this.models.workspaceUser.get(workspaceId, userId);

    if (!role) {
      throw new MemberNotFoundInSpace({ spaceId: workspaceId });
    }

    await this.ac
      .user(me.id)
      .workspace(workspaceId)
      .assert(
        role.type === WorkspaceRole.Admin
          ? 'Workspace.Administrators.Manage'
          : 'Workspace.Users.Manage'
      );

    await this.models.workspaceUser.delete(workspaceId, userId);

View on GitHub (pinned to 26c515e050)

Solutions

  1. Filter the current user out of the removable-members list in the UI before showing it.
  2. In bulk operations, skip entries where `userId === me.id` and log them instead of calling `revokeMember`.
  3. If you actually need to leave the workspace, call `leaveWorkspace` instead.
  4. Add a client-side guard: `if (userId === me.id) return;` before dispatching the mutation.

Example fix

// before
await sdk.revokeMember({ workspaceId, userId: selectedUserId });

// after
if (selectedUserId === me.id) {
  toast.error('You cannot revoke your own access. Use Leave Workspace instead.');
  return;
}
await sdk.revokeMember({ workspaceId, userId: selectedUserId });
Defensive patterns

Strategy: validation

Validate before calling

// Guard before dispatching the mutation
function canRevoke(meId, targetId) {
  return meId !== targetId;
}
if (!canRevoke(me.id, userId)) {
  notify('Use Leave Workspace to remove yourself.');
  return;
}

Type guard

function isSelfRevoke(meId, targetId) {
  return meId === targetId;
}

Try / catch

try {
  await sdk.revokeMember({ workspaceId, userId });
} catch (e) {
  if (e.code === 'can_not_revoke_yourself') {
    notify('You cannot revoke your own access. Use Leave Workspace.');
  } else throw e;
}

Prevention

When it happens

Trigger: Invoking `revokeMember(workspaceId, userId)` with `userId` set to the currently authenticated user's id — e.g., a UI that prefills the current user into the target field, or a script iterating over all member ids including the operator's own.

Common situations: A 'remove member' dropdown that accidentally lists the current user; bulk-remove tooling that iterates member ids without filtering out the caller; frontend bug passing `me.id` instead of the selected row's id.

Related errors


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