{"record":{"id":"306d1cabe7063a67","repo":"toeverything/AFFiNE","slug":"document-docid-not-found","errorCode":null,"errorMessage":"Document ${docId} not found","messagePattern":"Document (.+?) not found","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/doc/writer.ts","lineNumber":162,"sourceCode":"   * @param workspaceId - The workspace ID\n   * @param docId - The document ID to update\n   * @param markdown - The new markdown content\n   * @param editorId - Optional editor ID for tracking\n   */\n  async updateDoc(\n    workspaceId: string,\n    docId: string,\n    markdown: string,\n    editorId?: string\n  ): Promise<UpdateDocResult> {\n    this.logger.debug(\n      `Updating doc ${docId} in workspace ${workspaceId} from markdown`\n    );\n\n    // Fetch existing document\n    const existingDoc = await this.storage.getDoc(workspaceId, docId);\n    if (!existingDoc?.bin) {\n      throw new NotFoundException(`Document ${docId} not found`);\n    }\n\n    // Compute delta update using structural diff\n    // Use zero-copy buffer view when possible for native function\n    const existingBinary = Buffer.isBuffer(existingDoc.bin)\n      ? existingDoc.bin\n      : Buffer.from(\n          existingDoc.bin.buffer,\n          existingDoc.bin.byteOffset,\n          existingDoc.bin.byteLength\n        );\n    const delta = updateDocWithMarkdown(existingBinary, markdown, docId);\n\n    // Push only the delta changes\n    const timestamp = await this.storage.pushDocUpdates(\n      workspaceId,\n      docId,\n      [delta],","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/doc/writer.ts#L144-L180","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Have the client re-fetch the doc list and only allow 'update' on docIds present in the current list.","Treat 404 as 'doc deleted elsewhere' and surface a reconciliation prompt rather than letting the user retry blindly.","If the doc should exist, verify its snapshot row in storage and re-materialize if missing.","Return the docId in the error payload so the client can log/trace which doc failed."],"exampleFix":"// before\nconst existingDoc = await this.storage.getDoc(workspaceId, docId);\nif (!existingDoc?.bin) {\n  throw new NotFoundException(`Document ${docId} not found`);\n}\n\n// after\nconst existingDoc = await this.storage.getDoc(workspaceId, docId);\nif (!existingDoc) {\n  throw new NotFoundException(`Document ${docId} does not exist in workspace ${workspaceId}`);\n}\nif (!existingDoc.bin) {\n  throw new ConflictException(`Document ${docId} snapshot is empty; re-import required`);\n}","handlingStrategy":"validation","validationCode":"async function assertDocExists(storage, ws, doc) {\n  const d = await storage.getDoc(ws, doc);\n  if (!d?.bin) throw new UserError(`Doc ${doc} not found`);\n  return d;\n}","typeGuard":"import { NotFoundException } from '@nestjs/common';\nfunction isDocNotFound(e: unknown, docId: string): boolean {\n  return e instanceof NotFoundException && e.message.includes(`Document ${docId} not found`);\n}","tryCatchPattern":"try {\n  await writer.updateDoc(ws, doc, md);\n} catch (e) {\n  if (isDocNotFound(e, doc)) { res.status(404).send('Doc deleted elsewhere'); return; }\n  throw e;\n}","preventionTips":["Refresh the doc list before allowing edits.","Handle 404 by reconciling local state, not by silent retry.","Refrain from optimistic edits on docs that may have been removed."],"tags":["doc","update","not-found","validation"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}