toeverything/AFFiNE · error · NotFoundException

Document ${docId} not found

Error message

Document ${docId} not found

What it means

NotFoundException thrown by updateDoc (markdown path) when getDoc returns no bin for the given docId. The updater needs the existing binary to compute a structural delta via updateDocWithMarkdown; without it there is nothing to diff against. Correctly typed as NotFoundException.

Source

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

   * @param workspaceId - The workspace ID
   * @param docId - The document ID to update
   * @param markdown - The new markdown content
   * @param editorId - Optional editor ID for tracking
   */
  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 26c515e050)

Solutions

  1. Have the client re-fetch the doc list and only allow 'update' on docIds present in the current list.
  2. Treat 404 as 'doc deleted elsewhere' and surface a reconciliation prompt rather than letting the user retry blindly.
  3. If the doc should exist, verify its snapshot row in storage and re-materialize if missing.
  4. Return the docId in the error payload so the client can log/trace which doc failed.

Example fix

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

// after
const existingDoc = await this.storage.getDoc(workspaceId, docId);
if (!existingDoc) {
  throw new NotFoundException(`Document ${docId} does not exist in workspace ${workspaceId}`);
}
if (!existingDoc.bin) {
  throw new ConflictException(`Document ${docId} snapshot is empty; re-import required`);
}
Defensive patterns

Strategy: validation

Validate before calling

async function assertDocExists(storage, ws, doc) {
  const d = await storage.getDoc(ws, doc);
  if (!d?.bin) throw new UserError(`Doc ${doc} not found`);
  return d;
}

Type guard

import { NotFoundException } from '@nestjs/common';
function isDocNotFound(e: unknown, docId: string): boolean {
  return e instanceof NotFoundException && e.message.includes(`Document ${docId} not found`);
}

Try / catch

try {
  await writer.updateDoc(ws, doc, md);
} catch (e) {
  if (isDocNotFound(e, doc)) { res.status(404).send('Doc deleted elsewhere'); return; }
  throw e;
}

Prevention

When it happens

Trigger: Updating a docId that doesn't exist in the workspace, was deleted, or whose snapshot bin is empty. Cross-workspace docId reuse. Updating immediately after deletion before the client refreshed.

Common situations: Stale client holding a reference to a deleted doc. Doc was moved/merged and the old id no longer resolves. Storage partial write left the row without bin. Race between delete and update.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/306d1cabe7063a67. Report an issue: GitHub.