toeverything/AFFiNE · error · NotFoundException

Document not found

Error message

Document ${docId} not found

What it means

DocWriter.updateDoc fetches the existing doc binary from storage to compute a structural markdown diff (updateDocWithMarkdown). If getDoc returns no bin for (workspaceId, docId) it throws Nest NotFoundException (HTTP 404) with the docId in the message — the target of the update does not exist.

Solutions

  1. Verify the (workspaceId, docId) pair via a doc read API before updating
  2. If the doc is genuinely missing, call createDoc instead of updateDoc
  3. Refresh the client's doc list to drop deleted ids
  4. Make sure the environment/db the writer targets is the one where the doc lives

Example fix

// before
await docWriter.updateDoc(wsId, docId, markdown);

// after
const existing = await storage.getDoc(wsId, docId);
if (!existing?.bin) {
  return docWriter.createDoc(wsId, title, markdown); // or surface 404 to caller
}
await docWriter.updateDoc(wsId, docId, markdown);
Defensive patterns

Strategy: validation

Validate before calling

const existing = await storage.getDoc(workspaceId, docId);
if (!existing?.bin) {
  return docWriter.createDoc(workspaceId, title, markdown); // upsert-style fallback
}
await docWriter.updateDoc(workspaceId, docId, markdown);

Type guard

function isDocMissing404(e: unknown): boolean {
  const err = e as { status?: number; message?: string; response?: { status?: number } };
  return (err.response?.status === 404 || err.status === 404) && /not found/i.test(err.message ?? '');
}

Try / catch

try {
  await docWriter.updateDoc(wsId, docId, markdown);
} catch (e) {
  if (isDocMissing404(e)) return handleDocDeleted(docId); // refresh list / recreate
  throw e;
}

Prevention

When it happens

Trigger: Updating a doc that was deleted in another session; passing a client-generated/temporary docId that was never persisted; wrong workspaceId so the (workspaceId, docId) pair matches nothing; doc created only locally by the client but never synced.

Common situations: Stale editor state after a doc is moved or deleted; import pipelines re-running against a cleaned database; copy-pasted docIds between environments (dev vs prod).

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/306d1cabe7063a67. Report an issue: GitHub.

Appendix: source

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

   * @param workspaceId - The workspace ID
   * @param docId - The document ID to update
   * @param markdown - The new markdown content
   * @param editorId - Actor user ID
   */
  async updateDoc(
    workspaceId: string,
    docId: string,
    markdown: string,
    editorId: string
  ): Promise<UpdateDocResult> {
    this.logger.debug(
      `Updating doc ${docId} in workspace ${workspaceId} from markdown`
    );

    // Fetch existing document
    const existingDoc = await this.storage.getDoc(workspaceId, docId);
    if (!existingDoc?.bin) {
      throw new NotFoundException(`Document ${docId} not found`);
    }

    // Compute delta update using structural diff
    // Use zero-copy buffer view when possible for native function
    const existingBinary = Buffer.isBuffer(existingDoc.bin)
      ? existingDoc.bin
      : Buffer.from(
          existingDoc.bin.buffer,
          existingDoc.bin.byteOffset,
          existingDoc.bin.byteLength
        );
    const delta = updateDocWithMarkdown(existingBinary, markdown, docId);

    // Push only the delta changes
    const timestamp = await this.storage.pushDocUpdates(
      workspaceId,
      docId,
      [delta],

View on GitHub (pinned to 2af30773ae)