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
- Verify the workspace exists via the workspace API before calling createDoc
- Open the workspace once in the client app (or run the bootstrap flow) so the root doc is created
- Check the docs table for a row where spaceId = docId = workspaceId to confirm root doc presence
- 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
- Bootstrap workspaces through the normal creation flow so the root doc always exists
- Verify (workspaceId, root doc) presence in test fixtures
- Never create workspaces by direct SQL inserts in tests or seeds
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)