toeverything/AFFiNE · error · OwnerCanNotLeaveWorkspace

owner_can_not_leave_workspace

owner_can_not_leave_workspace

Error message

Owner can not leave the workspace.

What it means

Thrown by leaveWorkspace when the caller's role type is WorkspaceRole.Owner. AFFiNE forbids the last controlling owner from abandoning a workspace via leave, because the workspace would be left ownerless.

Solutions

  1. Transfer ownership to another member first (workspace settings → transfer ownership), then leave as a regular member.
  2. If you want the workspace gone, delete the workspace instead of leaving it.
  3. If ownership transfer already happened, sign out/in or refresh the session so the updated role is used.
Defensive patterns

Strategy: validation

Validate before calling

// Before showing/calling leave, check the user's role in the workspace
const { workspace } = await gql.request(GET_WORKSPACE, { id: workspaceId });
if (workspace.permissions.role === 'Owner') {
  // offer 'transfer ownership' or 'delete workspace' instead of leave
  showOwnerLeaveGuard();
} else {
  await gql.request(LEAVE_WORKSPACE, { workspaceId });
}

Type guard

const isWorkspaceOwner = (role?: string): boolean => role === 'Owner';

Try / catch

try {
  await leaveWorkspace(workspaceId);
} catch (e) {
  if (getErrorCode(e) === 'owner_can_not_leave_workspace') {
    openTransferOwnershipDialog(workspaceId);
  }
}

Prevention

When it happens

Trigger: The workspace owner account calls leaveWorkspace(workspaceId) for a workspace where its role.type === 'Owner'.

Common situations: Owner tries to leave during account cleanup or team handover; automation/test scripts that create a workspace and then try to leave it with the owner identity.

Related errors


AI-assisted analysis of toeverything/AFFiNE@4953682779 (2026-08-18). Data as JSON: /api/errors/bde269f0ada9f6cc. Report an issue: GitHub.

Appendix: source

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

      nullable: true,
      deprecationReason: 'no used anymore',
    })
    _sendLeaveMail?: boolean,
    @Args('workspaceName', {
      nullable: true,
      deprecationReason: 'no longer used',
    })
    _workspaceName?: string
  ) {
    const role = await this.models.workspaceUser.getActive(
      workspaceId,
      user.id
    );
    if (!role) {
      throw new MemberNotFoundInSpace({ spaceId: workspaceId });
    }
    if (role.type === WorkspaceRole.Owner) {
      throw new OwnerCanNotLeaveWorkspace();
    }

    await this.models.workspaceUser.delete(workspaceId, user.id);
    this.event.emit('workspace.members.leave', {
      workspaceId,
      userId: user.id,
    });

    this.event.emit('workspace.members.updated', {
      workspaceId,
    });
    await this.policy.reconcileWorkspaceQuotaState(workspaceId);

    return true;
  }

  private async acceptInvitationByEmail(role: WorkspaceUserCompat) {
    await this.assertWorkspaceAcceptsMemberChange(role.workspaceId);

View on GitHub (pinned to 4953682779)