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
- Filter the current user out of the removable-members list in the UI before showing it.
- In bulk operations, skip entries where `userId === me.id` and log them instead of calling `revokeMember`.
- If you actually need to leave the workspace, call `leaveWorkspace` instead.
- 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
- Exclude the current user from the removable-members list in the UI.
- In bulk flows, filter out entries where userId === operator id.
- Direct self-removal at `leaveWorkspace` instead.
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
- owner_can_not_leave_workspace
- space_access_denied
- space_access_denied
- invalid_invitation
- authentication_required
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/5bcfdf435f493b56.
Report an issue: GitHub.