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

publishDoc rejects any call where docId === workspaceId. In AFFiNE the workspace root id equals the workspace id and the root is a Space container, not a publishable page, so publishing 'the workspace id itself' is always a client mistake and is refused with ExpectToPublishDoc before any permission check.

Solutions

  1. Pass the actual page id: assert docId !== workspaceId in the client before calling publishDoc
  2. If the target really is the workspace root, pick a child page — the root itself cannot be published
  3. Check where the client sources docId from; it must come from a page/doc object, not the workspace

Example fix

// before
await publishDoc(ws.id, ws.id, mode); // root is a Space -> EXPECT_TO_PUBLISH_DOC

// after
const page = pickPageToPublish(ws); // a real page id, never the workspace id
if (page.id === ws.id) throw new Error('cannot publish the workspace root');
await publishDoc(ws.id, page.id, mode);
Defensive patterns

Strategy: validation

Validate before calling

// Never publish the workspace root
function assertPublishablePageId(workspaceId: string, docId: string) {
  if (!docId || docId === workspaceId) {
    throw new Error('docId must be a page id, not the workspace id');
  }
}
assertPublishablePageId(ws.id, docId);
await publishDoc(ws.id, docId, mode);

Type guard

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

Try / catch

try {
  await publishDoc(ws.id, docId, mode);
} catch (e) {
  if (isExpectToPublishDoc(e)) {
    promptUserToSelectAnActualPage(); // root can never be published
  } else throw e;
}

Prevention

When it happens

Trigger: Calling publishDoc(workspaceId, docId) with docId set to the workspace id — typically a client passing the root page id, defaulting docId to the workspace id, or reading the id from the wrong field of a workspace object.

Common situations: Porting older client code where the root page was addressed by the workspace id; constructing the mutation arguments from a workspace object instead of a page object.

Related errors


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

Appendix: source

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

  @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.inviteQuota.assertWorkspaceActionAllowed({
      actorUserId: 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 b6de0ad51b)