siyuan-note/siyuan · critical

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

Error message

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

What it means

For encrypted notebooks, fixTreeJSONData enforces fail-closed identity: the base-name ID of the .sy path must equal the root block ID of the decrypted content. A mismatch proves the ciphertext was swapped, the file renamed, or AAD authentication bypassed, so it is never silently auto-fixed (unencrypted trees get their ID reset instead).

Solutions

  1. Rename the .sy file back to the ID matching its root block ID (root.ID.sy).
  2. Restore the original encrypted file from backup or sync history; do not hand-edit ciphertext.
  3. If the mismatch is intentional, re-encrypt: export the document and re-import it into the encrypted notebook.

Example fix

// before: notebook/20240101-newname.sy decrypts to root.ID 20240101-oldname
// after: mv 20240101-newname.sy 20240101-oldname.sy
Defensive patterns

Strategy: validation

Validate before calling

pathID := util.GetTreeID(p)
// before writing into an encrypted notebook: ensure filename == root.ID + ".sy"

Type guard

func encryptedIdentityOK(pathID, rootID string) bool { return pathID == rootID }

Try / catch

if _, err := filesys.LoadTreeWithFix(box, p, lute); err != nil && strings.Contains(err.Error(), "base id") {
    // fail closed: do NOT auto-fix; restore the original encrypted file
}

Prevention

When it happens

Trigger: LoadTreeWithFix on an encrypted notebook where a .sy file was renamed, its ciphertext replaced with another document's, or where the decryption produced content whose Root.ID differs from the path-derived ID.

Common situations: Copying .sy files between notebooks and renaming them in an encrypted notebook, tampering attempts, or restoring encrypted files from a partial/mixed backup.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at kernel/filesys/tree.go:661

		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 treenode.FixInvalidListChildren(ret.Root) {
		needFix = true
	}
	if treenode.NormalizeTabs(ret.Root) {
		needFix = true
	}

	if !needFix {
		return jsonData, false, nil

View on GitHub (pinned to 9f775e8a12)