{"record":{"id":"78a36441b047c740","repo":"toeverything/AFFiNE","slug":"workspace-workspaceid-not-found-or-has-no-root","errorCode":null,"errorMessage":"Workspace ${workspaceId} not found or has no root document","messagePattern":"Workspace (.+?) not found or has no root document","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/doc/writer.ts","lineNumber":64,"sourceCode":"   * Creates a new document from markdown content.\n   *\n   * @param workspaceId - The workspace ID\n   * @param title - The document title\n   * @param markdown - The markdown content (body only)\n   * @param editorId - Optional editor ID for tracking\n   * @returns The created document ID\n   */\n  async createDoc(\n    workspaceId: string,\n    title: string,\n    markdown: string,\n    editorId?: string\n  ): Promise<CreateDocResult> {\n    // Fetch workspace root doc first - reject if not found\n    // The root doc (docId = workspaceId) contains meta.pages array\n    const rootDoc = await this.storage.getDoc(workspaceId, workspaceId);\n    if (!rootDoc?.bin) {\n      throw new NotFoundException(\n        `Workspace ${workspaceId} not found or has no root document`\n      );\n    }\n\n    const rootDocBin = Buffer.isBuffer(rootDoc.bin)\n      ? rootDoc.bin\n      : Buffer.from(\n          rootDoc.bin.buffer,\n          rootDoc.bin.byteOffset,\n          rootDoc.bin.byteLength\n        );\n\n    const docId = nanoid();\n\n    this.logger.debug(\n      `Creating doc ${docId} in workspace ${workspaceId} from markdown`\n    );\n","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/doc/writer.ts#L46-L82","documentation":"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).","triggerScenarios":"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).","commonSituations":"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).","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."],"exampleFix":"// before\nconst rootDoc = await this.storage.getDoc(workspaceId, workspaceId);\nif (!rootDoc?.bin) {\n  throw new NotFoundException(`Workspace ${workspaceId} not found or has no root document`);\n}\n\n// after — distinguish 'no workspace' from 'corrupt root' for cleaner ops\nconst rootDoc = await this.storage.getDoc(workspaceId, workspaceId);\nif (!rootDoc) {\n  throw new NotFoundException(`Workspace ${workspaceId} not found`);\n}\nif (!rootDoc.bin) {\n  this.logger.error(`Workspace ${workspaceId} root doc has empty bin`);\n  throw new ConflictException(`Workspace ${workspaceId} root document is corrupt`);\n}","handlingStrategy":"validation","validationCode":"async function assertWorkspaceReady(storage, ws) {\n  const root = await storage.getDoc(ws, ws);\n  if (!root?.bin) {\n    throw new UserError(`Workspace ${ws} not initialized`);\n  }\n  return root;\n}","typeGuard":"import { NotFoundException } from '@nestjs/common';\nfunction isWorkspaceMissing(e: unknown): boolean {\n  return e instanceof NotFoundException && /Workspace .* not found/.test(e.message);\n}","tryCatchPattern":"try {\n  await writer.createDoc(ws, title, md);\n} catch (e) {\n  if (isWorkspaceMissing(e)) return res.status(404).send('Workspace not found');\n  throw e;\n}","preventionTips":["Verify workspace existence before rendering the 'new doc' UI.","Validate workspaceId format before sending.","Re-initialize corrupt root docs detected by ops tooling."],"tags":["workspace","doc","not-found","validation","create"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}