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

  1. Guard the call: if (!(await docModel.isPublic(ws, doc))) return; before unpublish.
  2. Make the unpublish control conditional on the doc's published state in the UI.
  3. Catch doc_is_not_public and treat as success (idempotent unpublish).
  4. 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

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


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