toeverything/AFFiNE · error · ExpectToRevokePublicDoc

expect_to_revoke_public_doc

expect_to_revoke_public_doc

Error message

Expect doc not to be workspace

What it means

Thrown by revokePublicDoc when workspaceId === docId. Same rationale as publishDoc: the workspace root cannot be 'unpublished', so the server refuses with ExpectToRevokePublicDoc (invalid_input) and logs at error level.

Source

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

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

    return doc;
  }

  @Mutation(() => DocType)
  async revokePublicDoc(
    @CurrentUser() user: CurrentUser,
    @Args('workspaceId') workspaceId: string,
    @Args('docId') docId: string
  ) {
    if (workspaceId === docId) {
      this.logger.error('Expect to revoke public doc, but it is a workspace', {
        workspaceId,
        docId,
      });
      throw new ExpectToRevokePublicDoc('Expect doc not to be workspace');
    }

    await this.ac.user(user.id).doc(workspaceId, docId).assert('Doc.Publish');

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

    this.logger.log(`Revoke public doc ${docId} in workspace ${workspaceId}`);

    return doc;
  }

  private async tryFixDocOwner(workspaceId: string, docId: string) {
    const allowed = await this.cache.setnx(
      `fixingOwner:${workspaceId}:${docId}`,
      1,
      // TODO(@forehalo): we definitely need a timer helper
      { ttl: 1000 * 60 * 60 * 24 }

View on GitHub (pinned to 26c515e050)

Solutions

  1. Pass the actual public child page id as docId, not the workspace id.
  2. Disable the revoke action client-side when docId === workspaceId.
  3. Fix the data source that presented the workspace root as a public doc.

Example fix

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

// after
if (docId === workspaceId) return; // nothing to revoke
await gql.revokePublicDoc({ workspaceId, docId });
Defensive patterns

Strategy: validation

Validate before calling

// Never revoke on the workspace root
if (docId === workspaceId) {
  throw new Error('Cannot revoke public state of the workspace root');
}
await gql.revokePublicDoc({ workspaceId, docId });

Type guard

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

Try / catch

try {
  await gql.revokePublicDoc({ workspaceId, docId });
} catch (e) {
  if (isExpectToRevokePublicDoc(e)) {
    reportBug('revokePublicDoc called with workspaceId as docId');
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling revokePublicDoc with docId equal to workspaceId - attempting to revoke publication of the workspace root doc.

Common situations: UI bug offering a 'revoke public' action on the workspace root; reused id variable; client not filtering the workspace id out of revocable docs.

Related errors


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