toeverything/AFFiNE · error · NotFoundException

Workspace not found or has no root document

Error message

Workspace ${workspaceId} not found or has no root document

What it means

DocWriter.createDoc (used for markdown import and programmatic doc creation) requires the workspace root doc — the doc whose docId equals the workspaceId, which holds the meta.pages array — to register the new page. If storage.getDoc(workspaceId, workspaceId) returns no binary, it throws Nest NotFoundException (HTTP 404) with the workspaceId in the message.

Solutions

  1. Verify the workspace exists via the workspace API before calling createDoc
  2. Open the workspace once in the client app (or run the bootstrap flow) so the root doc is created
  3. Check the docs table for a row where spaceId = docId = workspaceId to confirm root doc presence
  4. For tests/seeds, create workspaces through the proper service calls rather than direct DB inserts

Example fix

// before
const { docId } = await docWriter.createDoc(wsId, title, markdown);

// after
const root = await storage.getDoc(wsId, wsId);
if (!root?.bin) throw new Error(`workspace ${wsId} not bootstrapped; open it in the client first`);
const { docId } = await docWriter.createDoc(wsId, title, markdown);
Defensive patterns

Strategy: validation

Validate before calling

// ensure workspace + root doc exist before creating docs programmatically
const root = await storage.getDoc(workspaceId, workspaceId);
if (!root?.bin) throw new Error(`workspace ${workspaceId} not bootstrapped`);
await docWriter.createDoc(workspaceId, title, markdown);

Type guard

// server-side
import { NotFoundException } from '@nestjs/common';
function isRootDocMissing(e: unknown): boolean {
  return e instanceof NotFoundException && /root document/.test(String((e as NotFoundException).message));
}
// client-side (HTTP)
function is404(e: unknown): boolean {
  const err = e as { status?: number; response?: { status?: number } };
  return err.response?.status === 404 || err.status === 404;
}

Try / catch

try {
  await docWriter.createDoc(wsId, title, md);
} catch (e) {
  if (isRootDocMissing(e)) throw new BootstrapRequiredError(wsId); // actionable rethrow
  throw e;
}

Prevention

When it happens

Trigger: Calling createDoc with a workspaceId that does not exist; a workspace created through a seed/import script that never initialized its root doc; calling createDoc against a database that was partially restored (workspace rows present, doc rows missing).

Common situations: Automation/CLI tools creating docs in freshly provisioned workspaces before the client app has opened them once; wrong environment variable pointing the writer at a different (empty) database; tests that create workspaces directly in SQL instead of via the API.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of toeverything/AFFiNE@2af30773ae (2026-08-18). Data as JSON: /api/errors/78a36441b047c740. Report an issue: GitHub.

Appendix: source

Thrown at packages/backend/server/src/core/doc/writer.ts:64

   * Creates a new document from markdown content.
   *
   * @param workspaceId - The workspace ID
   * @param title - The document title
   * @param markdown - The markdown content (body only)
   * @param editorId - Actor user ID
   * @returns The created document ID
   */
  async createDoc(
    workspaceId: string,
    title: string,
    markdown: string,
    editorId: string
  ): Promise<CreateDocResult> {
    // Fetch workspace root doc first - reject if not found
    // The root doc (docId = workspaceId) contains meta.pages array
    const rootDoc = await this.storage.getDoc(workspaceId, workspaceId);
    if (!rootDoc?.bin) {
      throw new NotFoundException(
        `Workspace ${workspaceId} not found or has no root document`
      );
    }

    const rootDocBin = Buffer.isBuffer(rootDoc.bin)
      ? rootDoc.bin
      : Buffer.from(
          rootDoc.bin.buffer,
          rootDoc.bin.byteOffset,
          rootDoc.bin.byteLength
        );

    const docId = nanoid();

    this.logger.debug(
      `Creating doc ${docId} in workspace ${workspaceId} from markdown`
    );

View on GitHub (pinned to 2af30773ae)