toeverything/AFFiNE · error · CanNotBatchGrantDocOwnerPermissions
can_not_batch_grant_doc_owner_permissions
can_not_batch_grant_doc_owner_permissions
Error message
Can not batch grant doc owner permissions.
What it means
Thrown by DocPermissionModel.batchSetUserRoles (permission-write.ts:584) when role === DocRole.Owner. Doc ownership is a singular, invariant-guarded relationship (a doc must always retain >=1 user owner), so it cannot be applied to a list of users in one call. UserFriendlyError, code can_not_batch_grant_doc_owner_permissions, type invalid_input, HTTP 400.
Source
Thrown at packages/backend/server/src/models/permission-write.ts:584
create: {
workspaceId,
docId,
principalType: 'user',
principalId: userId,
role: docRoleToNew(role),
},
});
}
@Transactional()
async batchSetUserRoles(
workspaceId: string,
docId: string,
userIds: string[],
role: DocRole
) {
if (role === DocRole.Owner) {
throw new CanNotBatchGrantDocOwnerPermissions();
}
if (userIds.length === 0) {
return 0;
}
const grantRole = docRoleToNew(role);
for (const userId of userIds) {
await this.db.docGrant.upsert({
where: {
workspaceId_docId_principalType_principalId: {
workspaceId,
docId,
principalType: 'user',
principalId: userId,
},
},
update: {
role: grantRole,View on GitHub (pinned to 26c515e050)
Solutions
- Grant Owner to exactly one user via the single-user grant path (or the doc owner-transfer endpoint).
- For batch operations, choose Manager / Editor / Commenter / Reader instead.
- Hide 'Owner' from any batch role selector in the UI.
Example fix
// before await models.docPermission.batchSetUserRoles(wsId, docId, userIds, DocRole.Owner); // after await models.docPermission.batchSetUserRoles(wsId, docId, userIds, DocRole.Manager); await models.docPermission.grantUserRole(wsId, docId, singleUserId, DocRole.Owner);
Defensive patterns
Strategy: validation
Validate before calling
function assertBatchableDocRole(role: DocRole) {
if (role === DocRole.Owner) {
throw new Error('Cannot batch-grant Doc Owner; grant Owner to a single user instead.');
}
}
assertBatchableDocRole(role);
await models.docPermission.batchSetUserRoles(wsId, docId, userIds, role); Type guard
function isBatchableDocRole(role: DocRole): boolean {
return role !== DocRole.Owner;
} Prevention
- Reserve DocRole.Owner for the single-user grant/transfer path.
- Default batch role pickers to Manager or Editor.
- Validate role != Owner before invoking batchSetUserRoles.
When it happens
Trigger: Calling the batch doc-grant API/mutation (batchSetUserRoles) with role: DocRole.Owner and any list of userIds.
Common situations: The 'add members' dialog defaults the role to Owner when a doc has no owner; a bulk-import/seed script assigns owner to many users at once; the UI reuses the batch endpoint where it should use single-owner transfer.
Related errors
- Cannot remove the last doc owner grant.
- doc_action_denied
- expect_to_grant_doc_user_roles
- can_not_batch_grant_doc_owner_permissions
- Cannot remove the last active workspace owner.
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/2043742a94668548.
Report an issue: GitHub.