toeverything/AFFiNE · warning · 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 caller passes their own user id — you cannot revoke your own membership with this API. The self-check runs first, before the member lookup and permission assertions, so it fires even if you are not otherwise authorized. Code can_not_revoke_yourself, type action_forbidden, HTTP 403.
Solutions
- Skip or disable the revoke action on the current user's row in the UI (show 'Leave workspace' instead if that is the intent).
- In scripts, filter out the acting user's id before calling revokeMember.
- If the goal is to leave the workspace, use the leave/transfer flow — owners must transfer ownership first (owner cannot leave).
Example fix
// before
for (const m of members) {
await revokeMember(ws.id, m.id); // includes me -> can_not_revoke_yourself
}
// after
for (const m of members.filter(m => m.id !== me.id)) {
await revokeMember(ws.id, m.id);
} Defensive patterns
Strategy: validation
Validate before calling
// never point revoke at yourself
if (targetUserId === me.id) {
disableRevokeButton(); // or route to a Leave-workspace flow instead
} Type guard
function isCanNotRevokeYourself(e: unknown): boolean {
const ext = (e as { extensions?: Record<string, unknown> })?.extensions;
return String(ext?.name ?? '').toLowerCase() === 'can_not_revoke_yourself';
} Try / catch
try {
await revokeMember(workspaceId, targetUserId);
} catch (e) {
if (isCanNotRevokeYourself(e)) {
// row action bound to the wrong id — hide remove on the 'you' row
} else throw e;
} Prevention
- Disable the remove action on the current user's row in the members table.
- Bulk-revoke scripts must skip the operator's own id.
When it happens
Trigger: Calling revokeMember(workspaceId, me.id) — typically a member-management UI whose row action is accidentally bound to the current user's row, or an automated cleanup script iterating all member ids including the operator's.
Common situations: Remove buttons not disabled on the 'you' row in the members table; batch scripts that revoke everyone then re-add, forgetting to skip the caller; owners trying to leave via revoke instead of a leave-workspace flow.
Related errors
AI-assisted analysis of toeverything/AFFiNE@4953682779 (2026-08-18).
Data as JSON: /api/errors/5bcfdf435f493b56.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/workspaces/resolvers/member.ts:602
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 4953682779)