toeverything/AFFiNE · warning · DocIsNotPublic
doc_is_not_public
doc_is_not_public
Error message
Doc is not public.
What it means
DocIsNotPublic (bad_request / doc_is_not_public) thrown by DocModel.unpublish (packages/backend/server/src/models/doc.ts:597). unpublish first calls isPublic(workspaceId, docId); if the doc is not currently published, there is nothing to unpublish and the guard throws before upsertMeta sets public:false / publishedAt:null.
Source
Thrown at packages/backend/server/src/models/doc.ts:597
/**
* Publish a doc as public.
*/
async publish(
workspaceId: string,
docId: string,
mode: PublicDocMode = PublicDocMode.Page
) {
return await this.upsertMeta(workspaceId, docId, {
public: true,
mode,
publishedAt: new Date(),
});
}
@Transactional()
async unpublish(workspaceId: string, docId: string) {
if (!(await this.isPublic(workspaceId, docId))) {
throw new DocIsNotPublic();
}
return await this.upsertMeta(workspaceId, docId, {
public: false,
publishedAt: null,
});
}
/**
* Check if the doc is public.
*/
async isPublic(workspaceId: string, docId: string) {
const policy = await this.db.docAccessPolicy.findUnique({
where: { workspaceId_docId: { workspaceId, docId } },
select: { visibility: true, publicRole: true },
});
return policy?.visibility === 'public' && policy.publicRole === 'external';
}View on GitHub (pinned to 26c515e050)
Solutions
- Guard the call: if (!(await docModel.isPublic(ws, doc))) return; before unpublish.
- Make the unpublish control conditional on the doc's published state in the UI.
- Catch doc_is_not_public and treat as success (idempotent unpublish).
- Refresh doc meta before showing the unpublish button.
Example fix
// before
await docModel.unpublish(workspaceId, docId);
// after
if (await docModel.isPublic(workspaceId, docId)) {
await docModel.unpublish(workspaceId, docId);
} Defensive patterns
Strategy: validation
Validate before calling
if (!(await docModel.isPublic(workspaceId, docId))) {
return; // already private — nothing to unpublish
}
await docModel.unpublish(workspaceId, docId); Type guard
const isPublicDoc = async (ws: string, doc: string): Promise<boolean> => await docModel.isPublic(ws, doc);
Try / catch
try {
await docModel.unpublish(workspaceId, docId);
} catch (e) {
if (e instanceof UserFriendlyError && e.code === 'doc_is_not_public') return; // idempotent
throw e;
} Prevention
- Make the 'Unpublish' control conditional on the doc's published state.
- Treat unpublish as idempotent — a not-public doc is the goal state.
- Refresh doc meta before toggling publish state.
When it happens
Trigger: Calling unpublish(workspaceId, docId) on a doc whose meta does not have public:true. isPublic returns false (no public meta row or public=false), so the operation aborts.
Common situations: User clicks 'Unpublish' on a doc that was never published or was already unpublished; double-submit of the unpublish action; UI state desync showing a doc as public when it is not; race between two unpublish calls.
Related errors
- ErrorCode.NoRootModelError
- Can not find the version to rollback to.
- Can not find the current version of the doc.
- Workspace ${workspaceId} not found or has no root document
- Document ${docId} not found
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/f0ccc1d2c826f5df.
Report an issue: GitHub.