siyuan-note/siyuan · error

tree is empty

Error message

tree is empty

What it means

Returned by `filesys.NormalizeTreeForRead` when the tree or its `Root` is nil. The function applies read-only normalization (spec check, spec upgrade, attribute escaping) and cannot proceed on an empty tree, so it fails fast rather than nil-dereferencing.

Source

Thrown at kernel/filesys/tree.go:225

		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
	}
	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
	}
	ret.Path = p
	ret.Root.Path = p

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Check the tree and error from `LoadTree`/`LoadTreeByData` before passing the tree to `NormalizeTreeForRead`.
  2. If constructing trees in code, always set `tree.Root` before normalizing.
  3. Log and propagate the error so callers do not silently continue with no tree.

Example fix

// before
tree, _ := filesys.LoadTree(box, p, lute)
filesys.NormalizeTreeForRead(tree) // panics on nil
// after
tree, err := filesys.LoadTree(box, p, lute)
if err != nil || tree == nil {
    return err
}
if err := filesys.NormalizeTreeForRead(tree); err != nil {
    return err
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Guard the tree before normalizing:
if tree == nil || tree.Root == nil {
    return errors.New("tree is empty")
}

Type guard

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

Prevention

When it happens

Trigger: Calling `NormalizeTreeForRead` on a tree that failed to load (nil return) or on a freshly-allocated `parse.Tree` whose `Root` was never set. The check `nil == tree || nil == tree.Root` covers both.

Common situations: Chaining a load that returned a nil tree without checking the error; constructing a tree manually for testing and forgetting `Root`; a corrupted file that parsed to an empty tree.

Related errors


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