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
CanNotBatchGrantDocOwnerPermissions (invalid_input / can_not_batch_grant_doc_owner_permissions) thrown by DocUserModel.batchSetUserRoles (packages/backend/server/src/models/doc-user.ts:67). The batch API refuses to assign DocRole.Owner to multiple users at once — owner is a singular, privileged role that must be transferred, not batch-granted. Any call with role === DocRole.Owner throws immediately (after the empty-list short-circuit).
Source
Thrown at packages/backend/server/src/models/doc-user.ts:67
assert(role !== DocRole.Owner, 'Cannot set Owner role of a doc to a user.');
await this.models.docGrant.set(workspaceId, docId, userId, role);
return await this.get(workspaceId, docId, userId);
}
@Transactional()
async batchSetUserRoles(
workspaceId: string,
docId: string,
userIds: string[],
role: DocRole
) {
if (userIds.length === 0) {
return 0;
}
if (role === DocRole.Owner) {
throw new CanNotBatchGrantDocOwnerPermissions();
}
return await this.models.docGrant.batchSetUserRoles(
workspaceId,
docId,
userIds,
role
);
}
@Transactional()
async delete(workspaceId: string, docId: string, userId: string) {
await this.models.docGrant.delete(workspaceId, docId, userId);
}
@Transactional()
async deleteByUserId(userId: string) {
await this.db.docGrant.deleteMany({View on GitHub (pinned to 26c515e050)
Solutions
- Use the dedicated owner-transfer API (singular) to set a doc owner, not batchSetUserRoles.
- Filter Owner out of the role list before calling batch: use Admin/Editor/Viewer for batch grants.
- If you truly need multiple owners, revisit the model — doc ownership is intentionally singular.
- Catch the code can_not_batch_grant_doc_owner_permissions and surface 'Set owners one at a time' in the UI.
Example fix
// before
await docUserModel.batchSetUserRoles(ws, doc, userIds, DocRole.Owner);
// after
if (role === DocRole.Owner) throw new Error('Use the owner-transfer endpoint for Owner.');
await docUserModel.batchSetUserRoles(ws, doc, userIds, DocRole.Admin); Defensive patterns
Strategy: validation
Validate before calling
if (role === DocRole.Owner) {
throw new Error('Use the owner-transfer endpoint to set a doc Owner, not batchSetUserRoles');
}
await docUserModel.batchSetUserRoles(workspaceId, docId, userIds, role); Type guard
const isBatchableRole = (r: DocRole): boolean => r !== DocRole.Owner;
Try / catch
try {
await docUserModel.batchSetUserRoles(ws, doc, ids, role);
} catch (e) {
if (e instanceof UserFriendlyError && e.code === 'can_not_batch_grant_doc_owner_permissions') {
ui.warn('Set doc owners one at a time.');
return;
}
throw e;
} Prevention
- Filter Owner out of any batch role assignment.
- Route Owner through the singular owner-transfer API.
- Add a unit test asserting batchSetUserRoles rejects Owner.
When it happens
Trigger: Calling batchSetUserRoles(workspaceId, docId, userIds, DocRole.Owner) with a non-empty userIds list. The equality check `role === DocRole.Owner` fires and the function throws before delegating to models.docGrant.batchSetUserRoles.
Common situations: UI 'make owners' bulk action that reuses the batch endpoint; migrating permissions with a bulk script that includes the owner role; mistaken role mapping that maps 'admin' to DocRole.Owner in a batch.
Related errors
- New workspace owner must be an active member.
- Cannot grant Owner role of a workspace to a user.
- can_not_batch_grant_doc_owner_permissions
- ErrorCode.NoRootModelError
- Can not find the version to rollback to.
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/bbc7d824b45eb15b.
Report an issue: GitHub.