{"record":{"id":"ef7696f43c6ba2d7","repo":"siyuan-note/siyuan","slug":"conditional-replacement-of-encrypted-documents-is-not","errorCode":null,"errorMessage":"conditional replacement of encrypted documents is not supported","messagePattern":"conditional replacement of encrypted documents is not supported","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/filesys/tree_compare_write.go","lineNumber":23,"sourceCode":"package filesys\n\nimport (\n\t\"errors\"\n\n\t\"github.com/88250/lute/parse\"\n\t\"github.com/siyuan-note/siyuan/kernel/cache\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// WriteTreeIfUnchanged 仅在普通文档仍与扫描源一致时原子替换，防止批量操作覆盖并发修改。\nfunc WriteTreeIfUnchanged(tree *parse.Tree, original []byte) (uint64, error) {\n\t_, encrypted, release, err := acquireCryptoLease(tree.Box)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer release()\n\tif encrypted {\n\t\treturn 0, errors.New(\"conditional replacement of encrypted documents is not supported\")\n\t}\n\tdata, filePath, err := prepareWriteTree(tree)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif err = util.WriteFileIfUnchanged(filePath, original, data); err != nil {\n\t\treturn 0, err\n\t}\n\tcache.SetTreeDataInBox(tree.ID, tree.Box, data)\n\tafterWriteTree(tree)\n\treturn uint64(len(data)), nil\n}\n","sourceCodeStart":5,"sourceCodeEnd":36,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/filesys/tree_compare_write.go#L5-L36","documentation":"WriteTreeIfUnchanged performs a compare-and-swap style atomic replacement: it writes only if the file still matches the scanned original bytes. Encrypted .sy files are ciphertext produced by a separate envelope pipeline, so a byte-level conditional replacement is deliberately unsupported — the function returns this error instead of risking ciphertext/AAD inconsistency.","triggerScenarios":"Calling filesys.WriteTreeIfUnchanged with a tree whose Box is an encrypted notebook (acquireCryptoLease reports encrypted=true). Callers such as batch apply operations hit this for every doc in an encrypted box.","commonSituations":"Batch refactoring/bulk-update tooling that uses the conditional write API against an encrypted notebook, assuming parity with plain notebooks.","solutions":["For encrypted notebooks use filesys.WriteTree instead, which goes through the encrypted write pipeline.","Gate the caller: check the notebook's encryption status before choosing WriteTreeIfUnchanged.","If atomicity is required, implement concurrency control at a higher level (e.g. serialize via transactions) rather than byte-compare."],"exampleFix":"// before\nn, err := filesys.WriteTreeIfUnchanged(tree, original)\n// after\nif isEncryptedBox(tree.Box) {\n    err = filesys.WriteTree(tree)\n} else {\n    _, err = filesys.WriteTreeIfUnchanged(tree, original)\n}","handlingStrategy":"fallback","validationCode":"// Go: check encryption before choosing the write path\n_, encrypted, release, err := acquireCryptoLease(box)\n// if encrypted, skip WriteTreeIfUnchanged entirely","typeGuard":null,"tryCatchPattern":"n, err := filesys.WriteTreeIfUnchanged(tree, original)\nif err != nil && err.Error() == \"conditional replacement of encrypted documents is not supported\" {\n    err = filesys.WriteTree(tree) // fall back to plain write\n}","preventionTips":["Branch on notebook encryption status before bulk writes","Use WriteTree for encrypted boxes as the default in batch tooling","Test batch features against encrypted notebooks, not only plain ones"],"tags":["encryption","write","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}