toeverything/AFFiNE · error · NotFoundException
Workspace ${workspaceId} not found or has no root document
Error message
Workspace ${workspaceId} not found or has no root document What it means
NotFoundException thrown by createDoc when the workspace's root document (docId === workspaceId) has no bin. AFFiNE stores workspace metadata — including the pages index — inside the root doc, so a missing/empty root doc means the workspace either doesn't exist or wasn't initialized. This is correctly typed as NotFoundException (likely a 404 at the resolver).
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 - Optional editor ID for tracking
* @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 26c515e050)
Solutions
- Confirm the workspace exists and is initialized before offering the 'new doc' action (call the workspace resolver / check root doc presence).
- If the workspace should exist, inspect its root doc row in storage and re-initialize if bin is empty.
- On the client, treat 404 from createDoc as 'workspace gone' and refresh workspace state / re-fetch the workspace list.
- Add an id-shape guard (workspaceId is a nanoid/cuid of expected length) to reject obvious garbage early.
Example fix
// before
const rootDoc = await this.storage.getDoc(workspaceId, workspaceId);
if (!rootDoc?.bin) {
throw new NotFoundException(`Workspace ${workspaceId} not found or has no root document`);
}
// after — distinguish 'no workspace' from 'corrupt root' for cleaner ops
const rootDoc = await this.storage.getDoc(workspaceId, workspaceId);
if (!rootDoc) {
throw new NotFoundException(`Workspace ${workspaceId} not found`);
}
if (!rootDoc.bin) {
this.logger.error(`Workspace ${workspaceId} root doc has empty bin`);
throw new ConflictException(`Workspace ${workspaceId} root document is corrupt`);
} Defensive patterns
Strategy: validation
Validate before calling
async function assertWorkspaceReady(storage, ws) {
const root = await storage.getDoc(ws, ws);
if (!root?.bin) {
throw new UserError(`Workspace ${ws} not initialized`);
}
return root;
} Type guard
import { NotFoundException } from '@nestjs/common';
function isWorkspaceMissing(e: unknown): boolean {
return e instanceof NotFoundException && /Workspace .* not found/.test(e.message);
} Try / catch
try {
await writer.createDoc(ws, title, md);
} catch (e) {
if (isWorkspaceMissing(e)) return res.status(404).send('Workspace not found');
throw e;
} Prevention
- Verify workspace existence before rendering the 'new doc' UI.
- Validate workspaceId format before sending.
- Re-initialize corrupt root docs detected by ops tooling.
When it happens
Trigger: Calling createDoc with a workspaceId that was never created, was deleted, or whose root doc failed to initialize. Also when the storage adapter returns the row but with an empty/null bin (corrupted or half-written root).
Common situations: Client sends a stale or fabricated workspaceId. A workspace creation job didn't finish writing the root doc. Storage migration left the root doc bin empty. Wrong workspace scoping (e.g. using a docId as workspaceId).
Related errors
- Can not find the version to rollback to.
- Document ${docId} not found
- doc_history_not_found
- doc_not_found
- Can not find the current version of the doc.
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/78a36441b047c740.
Report an issue: GitHub.