toeverything/AFFiNE · error · ExpectToPublishDoc

expect_to_publish_doc

expect_to_publish_doc

Error message

Expected to publish a doc, not a Space.

What it means

Thrown by the publishPage/publishDoc mutation when workspaceId === docId. In AFFiNE the workspaceId is itself the root doc id, so publishing 'the workspace doc' is meaningless - the server rejects it up front and logs at error level. ExpectToPublishDoc is an invalid_input error.

Source

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

  @Mutation(() => DocType)
  async publishDoc(
    @CurrentUser() user: CurrentUser,
    @Args('workspaceId') workspaceId: string,
    @Args('docId') docId: string,
    @Args({
      name: 'mode',
      type: () => PublicDocMode,
      nullable: true,
      defaultValue: PublicDocMode.Page,
    })
    mode: PublicDocMode
  ) {
    if (workspaceId === docId) {
      this.logger.error('Expect to publish doc, but it is a workspace', {
        workspaceId,
        docId,
      });
      throw new ExpectToPublishDoc();
    }

    await this.ac.user(user.id).doc(workspaceId, docId).assert('Doc.Publish');
    await this.assertCanShare(user.id, {
      workspaceId,
      docId,
      action: 'publishDoc',
    });

    const doc = await this.models.doc.publish(workspaceId, docId, mode);
    this.event.emit('doc.public_state.changed', { workspaceId, docId });

    this.logger.log(
      `Publish page ${docId} with mode ${mode} in workspace ${workspaceId}`
    );

    return doc;
  }

View on GitHub (pinned to 26c515e050)

Solutions

  1. Ensure the docId passed to publishDoc is a child page id, never the workspace root id.
  2. Add a client-side guard: disable the publish action when docId === workspaceId.
  3. Fix the upstream selection logic that surfaced the workspace root as a publishable doc.

Example fix

// before
await gql.publishDoc({ workspaceId, docId: workspaceId, mode });

// after - guard before calling
if (docId === workspaceId) {
  throw new Error('Cannot publish the workspace root doc');
}
await gql.publishDoc({ workspaceId, docId, mode });
Defensive patterns

Strategy: validation

Validate before calling

// Never publish the workspace root
if (docId === workspaceId) {
  throw new Error('Cannot publish the workspace root doc');
}
await gql.publishDoc({ workspaceId, docId, mode });

Type guard

function isExpectToPublishDoc(e: unknown): boolean {
  return (
    typeof e === 'object' &&
    e !== null &&
    (e as any).extensions?.code === 'expect_to_publish_doc'
  );
}

Try / catch

try {
  await gql.publishDoc({ workspaceId, docId, mode });
} catch (e) {
  if (isExpectToPublishDoc(e)) {
    // bug in selection logic; do not retry, fix the docId source
    reportBug('publishDoc called with workspaceId as docId');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling publishDoc with docId equal to workspaceId (e.g. passing the workspace root id where a child page id is expected).

Common situations: Frontend bug reusing the workspace id as the page id; user selecting the workspace root in a publish dialog; copy/paste of the workspace id into a doc publish field.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/9f754ec2408cdc51. Report an issue: GitHub.