siyuan-note/siyuan · error

tree is empty

Error message

tree is empty

What it means

NormalizeTreeForRead applies load-time normalization to a parsed tree without writing back. It first checks that the tree and its Root are non-nil and returns this error when the caller handed it an empty/unparsed tree. It is an input-state guard, not a disk problem.

Solutions

  1. Check the tree was actually parsed (parse the .sy content with lute) before normalizing.
  2. Verify the source file is non-empty and valid JSON .sy content.
  3. Guard the call site: skip normalization when tree == nil || tree.Root == nil.

Example fix

// before
NormalizeTreeForRead(tree)
// after
if tree == nil || tree.Root == nil {
    return errors.New("tree was not parsed")
}
NormalizeTreeForRead(tree)
Defensive patterns

Strategy: type-guard

Validate before calling

if (tree == null || tree.Root == null) { /* parse first or abort */ }

Type guard

func hasRoot(t *parse.Tree) bool { return t != nil && t.Root != nil }

Try / catch

if err := filesys.NormalizeTreeForRead(tree); err != nil {
    // tree empty or spec check failed; re-parse from disk
}

Prevention

When it happens

Trigger: Calling NormalizeTreeForRead with nil, with a parse.Tree whose Root is nil (e.g. lute parse failed or produced an empty tree), or with a zero-value struct.

Common situations: Parsing an empty or corrupted .sy file whose tree has no root; calling the API on a tree you constructed yourself but never populated; an upstream parse step silently failing and the nil tree being propagated.

Related errors


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

Appendix: source

Thrown at kernel/filesys/tree.go:229

		return
	}

	ret, err = LoadTreeByData(data, boxID, p, luteEngine)
	if nil == err {
		cache.SetTreeDataInBox(rootID, boxID, data)
	}
	return
}

func LoadTree(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) {
	ret, _, err = LoadTreeWithFix(boxID, p, luteEngine)
	return
}

// NormalizeTreeForRead 对只读树应用与文件加载一致的规范化处理,但不写回磁盘。
func NormalizeTreeForRead(tree *parse.Tree) (err error) {
	if nil == tree || nil == tree.Root {
		return errors.New("tree is empty")
	}
	if err = treenode.CheckSpec(tree); nil != err {
		return
	}
	if err = treenode.RefreshTableCellRichProjection(tree.Root); nil != err {
		return
	}
	treenode.UpgradeSpec(tree)
	escapeAttributeValues(tree)
	return
}

func LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) {
	ret, err = parseJSON2Tree(boxID, p, data, luteEngine)
	if nil != err {
		logging.LogErrorf("parse tree [%s] failed: %s", p, err)
		return
	}

View on GitHub (pinned to 9f775e8a12)