siyuan-note/siyuan · critical

encrypted .sy [%s]: base id [%s] != root id [%s]

Error message

encrypted .sy [%s]: base id [%s] != root id [%s]

What it means

Returned by `fixTreeJSONData` on encrypted notebooks when the ID parsed from the filename (`util.GetTreeID(p)`) does not equal the root block ID found inside the decrypted content. SiYuan treats this as a fail-closed integrity violation: a mismatch implies the ciphertext was replaced, the file was renamed, or AAD authentication was bypassed, so it refuses to silently rewrite the ID. Non-encrypted trees auto-fix the mismatch; encrypted trees do not.

Source

Thrown at kernel/filesys/tree.go:572

		return
	}

	if treenode.UpgradeSpec(ret) {
		needFix = true
	}

	// v3.5.1 https://github.com/siyuan-note/siyuan/pull/16657 引入的问题,属性值未转义
	// v3.5.2 https://github.com/siyuan-note/siyuan/issues/16686 进行了修复,并加了订正逻辑 https://github.com/siyuan-note/siyuan/pull/16712
	// https://github.com/siyuan-note/siyuan/security/advisories/GHSA-ff66-236v-p4fg XSS 漏洞:"title": "&\" onmouseenter=\"require('child_process').exec('calc')"
	if escapeAttributeValues(ret) {
		needFix = true
	}

	if pathID := util.GetTreeID(p); pathID != ret.Root.ID {
		if encrypted {
			// 加密 .sy:基名 ID(pathID)必须与解密后的根块 ID 一致。不一致说明密文被替换、
			// 文件名被篡改或 AAD 认证被绕过,不得静默修正——fail-closed,符合加密笔记本威胁模型。
			err = fmt.Errorf("encrypted .sy [%s]: base id [%s] != root id [%s]", p, pathID, ret.Root.ID)
			logging.LogErrorf("%s", err)
			return
		}
		needFix = true
		logging.LogInfof("reset tree id from [%s] to [%s]", ret.Root.ID, pathID)
		ret.Root.ID = pathID
		ret.ID = pathID
		ret.Root.SetIALAttr("id", ret.ID)
	}

	if !needFix {
		return jsonData, false, nil
	}

	renderer := render.NewJSONRenderer(ret, luteEngine.RenderOptions, luteEngine.ParseOptions)
	data = renderer.Render()

	if !util.UseSingleLineSave {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Do not rename or move encrypted `.sy` files by hand; use SiYuan's own move/rename so IDs stay consistent.
  2. Restore the affected document from an encrypted history snapshot taken before the mismatch.
  3. If the mismatch is from a known migration bug, re-encrypt or re-save the document through the kernel so filename and root ID realign.
Defensive patterns

Strategy: try-catch

Validate before calling

// For encrypted trees, verify filename-vs-rootID alignment before deeper processing:
if pathID := util.GetTreeID(p); encrypted && pathID != tree.Root.ID {
    return fmt.Errorf("encrypted .sy [%s]: base id [%s] != root id [%s]", p, pathID, tree.Root.ID)
}

Try / catch

tree, err := filesys.LoadTree(box, p, lute)
if err != nil && strings.Contains(err.Error(), "base id") {
    // integrity violation in encrypted box: do NOT auto-fix; restore from encrypted history
    return err
}

Prevention

When it happens

Trigger: Renaming an encrypted `.sy` file outside SiYuan; copying ciphertext from one document into another file; a tampered or corrupted decrypt that produced a different root ID; an AAD-rotation bug. The error fires only when `encrypted == true`.

Common situations: Manual file operations inside an encrypted notebook's data dir; a sync conflict resolved by file copy; an attempt to defeat the binding between filename and content in an encrypted box.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/0387450a19b3c111. Report an issue: GitHub.