{"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":"exception","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 - Actor user ID\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/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/doc/writer.ts#L46-L82","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nconst { docId } = await docWriter.createDoc(wsId, title, markdown);\n\n// after\nconst root = await storage.getDoc(wsId, wsId);\nif (!root?.bin) throw new Error(`workspace ${wsId} not bootstrapped; open it in the client first`);\nconst { docId } = await docWriter.createDoc(wsId, title, markdown);","handlingStrategy":"validation","validationCode":"// ensure workspace + root doc exist before creating docs programmatically\nconst root = await storage.getDoc(workspaceId, workspaceId);\nif (!root?.bin) throw new Error(`workspace ${workspaceId} not bootstrapped`);\nawait docWriter.createDoc(workspaceId, title, markdown);","typeGuard":"// server-side\nimport { NotFoundException } from '@nestjs/common';\nfunction isRootDocMissing(e: unknown): boolean {\n  return e instanceof NotFoundException && /root document/.test(String((e as NotFoundException).message));\n}\n// client-side (HTTP)\nfunction is404(e: unknown): boolean {\n  const err = e as { status?: number; response?: { status?: number } };\n  return err.response?.status === 404 || err.status === 404;\n}","tryCatchPattern":"try {\n  await docWriter.createDoc(wsId, title, md);\n} catch (e) {\n  if (isRootDocMissing(e)) throw new BootstrapRequiredError(wsId); // actionable rethrow\n  throw e;\n}","preventionTips":["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"],"tags":["doc-writer","workspace","not-found","markdown-import"],"backgroundTag":"entity-not-found","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}