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
revokePublicDoc rejects calls where workspaceId === docId, mirroring publishDoc: the workspace root is a Space, not a published doc, so revoking 'the workspace id itself' is a client mistake and throws ExpectToRevokePublicDoc before permissions are evaluated.
Solutions
- Guard client-side: only call revokePublicDoc when docId !== workspaceId
- Fix the data source that produced a docId equal to the workspace id
- If the goal was to unpublish everything, revoke each published page individually
Example fix
// before
await revokePublicDoc(ws.id, docIdFromLegacyRecord /* === ws.id */);
// after
if (docId === ws.id) {
// legacy record pointed at the root; nothing to revoke
return;
}
await revokePublicDoc(ws.id, docId); Defensive patterns
Strategy: validation
Validate before calling
// Skip revoke calls that target the root
if (docId !== ws.id) {
await revokePublicDoc(ws.id, docId);
} Type guard
function isExpectToRevokePublicDoc(e: unknown): boolean {
return (
typeof e === 'object' && e !== null &&
(e as { extensions?: { code?: string } }).extensions?.code === 'expect_to_revoke_public_doc'
);
} Try / catch
try {
await revokePublicDoc(ws.id, docId);
} catch (e) {
if (isExpectToRevokePublicDoc(e)) {
// legacy record pointed at the root; nothing to revoke, treat as success
return;
} else throw e;
} Prevention
- Sanitize legacy public-doc records whose docId equals the workspace id
- Apply one shared workspaceId !== docId guard to every doc-level mutation call site
- Log client-side when the guard trips to catch bad id sources early
When it happens
Trigger: Calling revokePublicDoc(workspaceId, docId) with the workspace id in both arguments — stale id from an older public-doc record, or a client defaulting docId to the workspace root.
Common situations: Migration code replaying public-doc lists that stored the workspace id as docId; UI passing the workspace root id for the 'home' page.
Related errors
- expect_to_publish_doc
- doc_default_role_can_not_be_owner
- expect_to_grant_doc_user_roles
- expect_to_revoke_doc_user_roles
- expect_to_update_doc_user_role
AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-18).
Data as JSON: /api/errors/5c813974b051f0f4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/workspaces/resolvers/doc.ts:463
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 b6de0ad51b)