toeverything/AFFiNE · error · ExpectToGrantDocUserRoles
expect_to_grant_doc_user_roles
expect_to_grant_doc_user_roles
Error message
Expect doc not to be workspace
What it means
Thrown by grantDocUserRoles when input.workspaceId === input.docId. Granting per-doc user roles on the workspace root is invalid (the root has no separate doc ACL), so the server rejects it with ExpectToGrantDocUserRoles (invalid_input), attaching { spaceId, docId } to extensions and logging at error level.
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:729
);
}
@Mutation(() => Boolean)
async grantDocUserRoles(
@CurrentUser() user: CurrentUser,
@Args('input') input: GrantDocUserRolesInput
): Promise<boolean> {
const pairs = {
spaceId: input.workspaceId,
docId: input.docId,
};
if (input.workspaceId === input.docId) {
this.logger.error(
'Expect to grant doc user roles, but it is a workspace',
pairs
);
throw new ExpectToGrantDocUserRoles(
pairs,
'Expect doc not to be workspace'
);
}
await this.ac.user(user.id).doc(input).assert('Doc.Users.Manage');
await this.models.docUser.batchSetUserRoles(
input.workspaceId,
input.docId,
input.userIds,
input.role
);
this.event.emit('doc.grants.changed', {
workspaceId: input.workspaceId,
docId: input.docId,
});
View on GitHub (pinned to 26c515e050)
Solutions
- Pass a real child page id as input.docId, distinct from input.workspaceId.
- Disable doc-level role grant in the UI when the selected node is the workspace root.
- Add a client-side precondition: input.workspaceId !== input.docId before invoking the mutation.
Example fix
// before
await gql.grantDocUserRoles({
input: { workspaceId, docId: workspaceId, userIds, role },
});
// after
if (docId === workspaceId) {
throw new Error('Use workspace member roles for the root, not doc roles');
}
await gql.grantDocUserRoles({
input: { workspaceId, docId, userIds, role },
}); Defensive patterns
Strategy: validation
Validate before calling
// Reject the input before calling the mutation
if (input.workspaceId === input.docId) {
throw new Error('Use workspace member roles for the root, not doc roles');
}
await gql.grantDocUserRoles({ input }); Type guard
function isExpectToGrantDocUserRoles(e: unknown): boolean {
return (
typeof e === 'object' &&
e !== null &&
(e as any).extensions?.code === 'expect_to_grant_doc_user_roles'
);
} Try / catch
try {
await gql.grantDocUserRoles({ input });
} catch (e) {
if (isExpectToGrantDocUserRoles(e)) {
// selection bug; do not retry, fix the docId source
reportBug('grantDocUserRoles called with workspaceId as docId');
return;
}
throw e;
} Prevention
- Disable the 'manage doc roles' action on the workspace root in the UI.
- Validate input.workspaceId !== input.docId in the client SDK before sending.
- Keep role-grant scopes typed (workspace roles vs doc roles) to prevent misuse.
When it happens
Trigger: Calling grantDocUserRoles with a GrantDocUserRolesInput whose docId equals workspaceId - the client is trying to grant doc-level roles on the workspace root.
Common situations: UI passes the workspace id as the docId for the root node; role-management dialog opened on the workspace root; client shares a docId variable across workspace and doc scopes.
Related errors
- expect_to_publish_doc
- expect_to_revoke_public_doc
- expect_to_revoke_doc_user_roles
- expect_to_update_doc_user_role
- doc_default_role_can_not_be_owner
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/686479db8dec8735.
Report an issue: GitHub.