siyuan-note/siyuan · error

conditional replacement of encrypted documents is not…

Error message

conditional replacement of encrypted documents is not supported

What it means

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.

Solutions

  1. For encrypted notebooks use filesys.WriteTree instead, which goes through the encrypted write pipeline.
  2. Gate the caller: check the notebook's encryption status before choosing WriteTreeIfUnchanged.
  3. If atomicity is required, implement concurrency control at a higher level (e.g. serialize via transactions) rather than byte-compare.

Example fix

// before
n, err := filesys.WriteTreeIfUnchanged(tree, original)
// after
if isEncryptedBox(tree.Box) {
    err = filesys.WriteTree(tree)
} else {
    _, err = filesys.WriteTreeIfUnchanged(tree, original)
}
Defensive patterns

Strategy: fallback

Validate before calling

// Go: check encryption before choosing the write path
_, encrypted, release, err := acquireCryptoLease(box)
// if encrypted, skip WriteTreeIfUnchanged entirely

Try / catch

n, err := filesys.WriteTreeIfUnchanged(tree, original)
if err != nil && err.Error() == "conditional replacement of encrypted documents is not supported" {
    err = filesys.WriteTree(tree) // fall back to plain write
}

Prevention

When it happens

Trigger: 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.

Common situations: Batch refactoring/bulk-update tooling that uses the conditional write API against an encrypted notebook, assuming parity with plain notebooks.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/ef7696f43c6ba2d7. Report an issue: GitHub.

Appendix: source

Thrown at kernel/filesys/tree_compare_write.go:23

package filesys

import (
	"errors"

	"github.com/88250/lute/parse"
	"github.com/siyuan-note/siyuan/kernel/cache"
	"github.com/siyuan-note/siyuan/kernel/util"
)

// WriteTreeIfUnchanged 仅在普通文档仍与扫描源一致时原子替换,防止批量操作覆盖并发修改。
func WriteTreeIfUnchanged(tree *parse.Tree, original []byte) (uint64, error) {
	_, encrypted, release, err := acquireCryptoLease(tree.Box)
	if err != nil {
		return 0, err
	}
	defer release()
	if encrypted {
		return 0, errors.New("conditional replacement of encrypted documents is not supported")
	}
	data, filePath, err := prepareWriteTree(tree)
	if err != nil {
		return 0, err
	}
	if err = util.WriteFileIfUnchanged(filePath, original, data); err != nil {
		return 0, err
	}
	cache.SetTreeDataInBox(tree.ID, tree.Box, data)
	afterWriteTree(tree)
	return uint64(len(data)), nil
}

View on GitHub (pinned to 9f775e8a12)