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
- Verify the (workspaceId, docId) pair via a doc read API before updating
- If the doc is genuinely missing, call createDoc instead of updateDoc
- Refresh the client's doc list to drop deleted ids
- 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
- Read-before-write: confirm the (workspaceId, docId) pair exists before updating
- Use server-provided docIds; never synthesize them client-side
- Drop deleted doc ids from local caches on 404
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
- Workspace not found or has no root document
- doc_history_not_found
- doc_not_found
- doc_not_found
- doc_not_found
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)