{"record":{"id":"2043742a94668548","repo":"toeverything/AFFiNE","slug":"can-not-batch-grant-doc-owner-permissions-204374","errorCode":"can_not_batch_grant_doc_owner_permissions","errorMessage":"Can not batch grant doc owner permissions.","messagePattern":"Can not batch grant doc owner permissions\\.","errorType":"error_code","errorClass":"CanNotBatchGrantDocOwnerPermissions","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/models/permission-write.ts","lineNumber":584,"sourceCode":"      create: {\n        workspaceId,\n        docId,\n        principalType: 'user',\n        principalId: userId,\n        role: docRoleToNew(role),\n      },\n    });\n  }\n\n  @Transactional()\n  async batchSetUserRoles(\n    workspaceId: string,\n    docId: string,\n    userIds: string[],\n    role: DocRole\n  ) {\n    if (role === DocRole.Owner) {\n      throw new CanNotBatchGrantDocOwnerPermissions();\n    }\n    if (userIds.length === 0) {\n      return 0;\n    }\n\n    const grantRole = docRoleToNew(role);\n    for (const userId of userIds) {\n      await this.db.docGrant.upsert({\n        where: {\n          workspaceId_docId_principalType_principalId: {\n            workspaceId,\n            docId,\n            principalType: 'user',\n            principalId: userId,\n          },\n        },\n        update: {\n          role: grantRole,","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/models/permission-write.ts#L566-L602","documentation":"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.","triggerScenarios":"Calling the batch doc-grant API/mutation (batchSetUserRoles) with role: DocRole.Owner and any list of userIds.","commonSituations":"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.","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."],"exampleFix":"// before\nawait models.docPermission.batchSetUserRoles(wsId, docId, userIds, DocRole.Owner);\n// after\nawait models.docPermission.batchSetUserRoles(wsId, docId, userIds, DocRole.Manager);\nawait models.docPermission.grantUserRole(wsId, docId, singleUserId, DocRole.Owner);","handlingStrategy":"validation","validationCode":"function assertBatchableDocRole(role: DocRole) {\n  if (role === DocRole.Owner) {\n    throw new Error('Cannot batch-grant Doc Owner; grant Owner to a single user instead.');\n  }\n}\n\nassertBatchableDocRole(role);\nawait models.docPermission.batchSetUserRoles(wsId, docId, userIds, role);","typeGuard":"function isBatchableDocRole(role: DocRole): boolean {\n  return role !== DocRole.Owner;\n}","tryCatchPattern":null,"preventionTips":["Reserve DocRole.Owner for the single-user grant/transfer path.","Default batch role pickers to Manager or Editor.","Validate role != Owner before invoking batchSetUserRoles."],"tags":["doc","permissions","batch","ownership"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}