{"record":{"id":"084a06c771b91025","repo":"toeverything/AFFiNE","slug":"doc-default-role-can-not-be-owner","errorCode":"doc_default_role_can_not_be_owner","errorMessage":"Doc default role can not be owner.","messagePattern":"Doc default role can not be owner\\.","errorType":"exception","errorClass":"DocDefaultRoleCanNotBeOwner","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/resolvers/doc.ts","lineNumber":914,"sourceCode":"        workspaceId: input.workspaceId,\n        docId: input.docId,\n      });\n      this.logger.log(`Update doc user role (${JSON.stringify(info)})`);\n    }\n\n    return true;\n  }\n\n  @Mutation(() => Boolean)\n  async updateDocDefaultRole(\n    @CurrentUser() user: CurrentUser,\n    @Args('input') input: UpdateDocDefaultRoleInput\n  ) {\n    if (input.role === DocRole.Owner) {\n      this.logger.debug(\n        `Doc default role can not be owner (${JSON.stringify(input)})`\n      );\n      throw new DocDefaultRoleCanNotBeOwner();\n    }\n    const pairs = {\n      spaceId: input.workspaceId,\n      docId: input.docId,\n    };\n    if (input.workspaceId === input.docId) {\n      this.logger.error(\n        'Expect to update page default role, but it is a workspace',\n        pairs\n      );\n      throw new ExpectToUpdateDocUserRole(\n        pairs,\n        'Expect doc not to be workspace'\n      );\n    }\n    const newRole =\n      input.role === DocRole.None ? 'none' : toDomainDocRole(input.role);\n    if (!newRole) {","sourceCodeStart":896,"sourceCodeEnd":932,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/workspaces/resolvers/doc.ts#L896-L932","documentation":"updateDocDefaultRole refuses role === DocRole.Owner. The default role is what every workspace member effectively gets on the doc; ownership must remain an explicit per-user grant, so making Owner the doc default is rejected with DocDefaultRoleCanNotBeOwner.","triggerScenarios":"Calling updateDocDefaultRole with input.role 'Owner' — typically a UI whose role dropdown lists every DocRole value including Owner, or code copying a member's Owner grant into the default-role field.","commonSituations":"Role pickers generated straight from the DocRole enum; settings forms that reuse the per-user role list for the default role.","solutions":["Choose Admin (or lower) as the default role and filter Owner out of the default-role options","To grant ownership, use grantDocUserRoles for specific users instead of the default role"],"exampleFix":"// before\nconst allRoles = Object.values(DocRole); // includes Owner\nawait updateDocDefaultRole({ workspaceId, docId, role: 'Owner' }); // -> DOC_DEFAULT_ROLE_CAN_NOT_BE_OWNER\n\n// after\nconst defaultRoleOptions = Object.values(DocRole).filter(r => r !== 'Owner');\nawait updateDocDefaultRole({ workspaceId, docId, role: 'Admin' });","handlingStrategy":"validation","validationCode":"// Filter Owner out of default-role options before the call\nconst DEFAULT_ROLE_BLACKLIST = ['Owner'];\nfunction isValidDefaultRole(role: DocRole): boolean {\n  return !DEFAULT_ROLE_BLACKLIST.includes(role);\n}\nif (!isValidDefaultRole(input.role)) {\n  throw new Error('default role cannot be Owner');\n}\nawait updateDocDefaultRole(input);","typeGuard":"function isDefaultRoleOwnerError(e: unknown): boolean {\n  return (\n    typeof e === 'object' && e !== null &&\n    (e as { extensions?: { code?: string } }).extensions?.code === 'doc_default_role_can_not_be_owner'\n  );\n}","tryCatchPattern":"try {\n  await updateDocDefaultRole(input);\n} catch (e) {\n  if (isDefaultRoleOwnerError(e)) {\n    input.role = 'Admin'; // nearest valid default; Owner stays a per-user grant\n    await updateDocDefaultRole(input);\n  } else throw e;\n}","preventionTips":["Generate the default-role dropdown from DocRole minus Owner","Keep per-user role assignment (grantDocUserRoles) as the only way to convey ownership","Validate roles against a whitelist at the form layer, not only server-side"],"tags":["doc","permissions","roles","validation","affine"],"backgroundTag":"invalid-role-assignment","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}