toeverything/AFFiNE · error · ExpectToUpdateDocUserRole
expect_to_update_doc_user_role
expect_to_update_doc_user_role
Error message
Expect doc not to be workspace
What it means
Thrown by the updateDocUserRole mutation when input.docId === input.workspaceId. Same root-doc invariant as revokeDocUserRole: the workspace root doc is addressed by workspace-level role APIs, not the doc-grant API. Rejecting early (invalid_input, HTTP 400) prevents a no-op or inconsistent state where doc-level owner/grant logic would run against the workspace root.
Source
Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:810
this.logger.log(`Revoke doc user roles (${JSON.stringify(info)})`);
return true;
}
@Mutation(() => Boolean)
async updateDocUserRole(
@CurrentUser() user: CurrentUser,
@Args('input') input: UpdateDocUserRoleInput
): Promise<boolean> {
const pairs = {
spaceId: input.workspaceId,
docId: input.docId,
};
if (input.workspaceId === input.docId) {
this.logger.error(
'Expect to update doc user role, but it is a workspace',
pairs
);
throw new ExpectToUpdateDocUserRole(
pairs,
'Expect doc not to be workspace'
);
}
const info = {
...pairs,
userId: input.userId,
role: input.role,
};
if (input.role === DocRole.Owner) {
await this.ac.user(user.id).doc(input).assert('Doc.TransferOwner');
await this.models.docUser.setOwner(
input.workspaceId,
input.docId,
input.userId
);View on GitHub (pinned to 26c515e050)
Solutions
- Supply the actual nested doc id as docId; keep workspaceId as the workspace id.
- To change a workspace member's role, call grantMember(workspaceId, userId, role) instead.
- Guard the caller: skip/redirect when workspaceId === docId.
Example fix
// before
updateDocUserRole({ workspaceId: ws.id, docId: ws.id, userId, role });
// after
updateDocUserRole({ workspaceId: ws.id, docId: page.id, userId, role }); Defensive patterns
Strategy: validation
Validate before calling
function assertUpdateDocUserRoleArgs(input: { workspaceId: string; docId: string }) {
if (input.workspaceId === input.docId) {
throw new Error('Cannot update doc role on the workspace root; use grantMember instead');
}
}
assertUpdateDocUserRoleArgs(input); Type guard
function isNestedDocInput(input: { workspaceId: string; docId: string }): boolean {
return Boolean(input.workspaceId) && Boolean(input.docId) && input.workspaceId !== input.docId;
} Prevention
- Bind docId and workspaceId to distinct values from the route.
- Route workspace-root permission changes through grantMember/revokeMember.
- Add a client-side guard that disables the action when the ids match.
When it happens
Trigger: Calling mutation updateDocUserRole with UpdateDocUserRoleInput whose docId equals workspaceId (the workspace root doc id passed as docId).
Common situations: A shared permission-management component binds both fields to the same workspace id when the user opens the workspace landing page; a script that iterates docs and accidentally includes the workspace root id; stale cached doc id equated to the workspace id.
Related errors
- expect_to_revoke_doc_user_roles
- doc_default_role_can_not_be_owner
- action_forbidden_on_non_team_workspace
- expect_to_grant_doc_user_roles
- action_forbidden
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/398e56a22968c9d5.
Report an issue: GitHub.