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

updateDocUserRole rejects input where workspaceId === docId, continuing the root-is-a-Space guard family. Changing a user's role 'on the workspace root' is not a doc-level operation, so the mutation throws ExpectToUpdateDocUserRole before the Doc.Users.Manage check.

Solutions

  1. Disable the role-change action for the root node and validate docId !== workspaceId before calling
  2. Ensure the selected-node id in the UI comes from a page node
  3. Use workspace member role APIs for workspace-wide changes

Example fix

// before
onRoleChange(node.id /* root node === ws.id */, userId, role);

// after
if (node.id === ws.id) return disableRoleChangeForRoot(node);
await updateDocUserRole({ workspaceId: ws.id, docId: node.id, userId, role });
Defensive patterns

Strategy: validation

Validate before calling

// Guard the update input before calling
if (input.workspaceId === input.docId) {
  throw new Error('cannot update a doc role on the workspace root');
}
await updateDocUserRole(input);

Type guard

function isExpectToUpdateDocUserRole(e: unknown): boolean {
  return (
    typeof e === 'object' && e !== null &&
    (e as { extensions?: { code?: string } }).extensions?.code === 'expect_to_update_doc_user_role'
  );
}

Try / catch

try {
  await updateDocUserRole(input);
} catch (e) {
  if (isExpectToUpdateDocUserRole(e)) {
    showSelectAPageError(); // role changes do not apply to the root
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateDocUserRole with input.docId === input.workspaceId — id taken from the wrong object, or a UI whose selected node is the workspace root.

Common situations: Role pickers rendered for the root node of a doc tree; refactors that swapped the argument order or default values.

Related errors


AI-assisted analysis of toeverything/AFFiNE@2af30773ae (2026-08-18). Data as JSON: /api/errors/398e56a22968c9d5. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:853

    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,
    };

    const role = toDomainDocRole(input.role);
    if (!role) {
      throw new ExpectToUpdateDocUserRole(pairs, 'Invalid doc role');
    }
    try {
      await this.runtime.executeDomainCommandV1({
        command: 'transition_doc_role',

View on GitHub (pinned to 2af30773ae)