siyuan-note/siyuan · error

parse tree [%s] failed

Error message

parse tree [%s] failed

What it means

Returned by RenderTemplate (template.go:425) when the template executed successfully but the resulting markdown bytes could not be parsed into a Lute/Kramdown tree by parseKTree(md). This is a post-render integrity failure: the template produced output that is not valid SiYuan markdown/Kramdown.

Source

Thrown at kernel/model/template.go:425

	goTpl = goTpl.Funcs(tplFuncMap)
	tpl, err := goTpl.Funcs(tplFuncMap).Parse(gulu.Str.FromBytes(md))
	if err != nil {
		err = fmt.Errorf(Conf.Language(44), err.Error())
		return
	}

	buf := &bytes.Buffer{}
	buf.Grow(4096)
	if err = tpl.Execute(buf, dataModel); err != nil {
		err = fmt.Errorf(Conf.Language(44), err.Error())
		return
	}
	md = buf.Bytes()
	tree = parseKTree(md)
	if nil == tree {
		msg := fmt.Sprintf("parse tree [%s] failed", p)
		logging.LogError(msg)
		err = errors.New(msg)
		return
	}

	var nodesNeedAppendChild, unlinks []*ast.Node
	// 模板内部块旧 ID 到新 ID 的映射,用于成套改写模板内部的自引用
	blockIDs := map[string]string{}
	ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
		if !entering {
			return ast.WalkContinue
		}

		if "" != n.ID {
			// 重新生成 ID,并记录旧 ID 到新 ID 的映射,用于后续成套改写模板内部的自引用
			oldID := n.ID
			n.ID = ast.NewNodeID()
			blockIDs[oldID] = n.ID
			n.SetIALAttr("id", n.ID)
			n.RemoveIALAttr(av.NodeAttrNameAvs)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Capture the executed markdown output (buf.Bytes()) before parseKTree and inspect it for malformed Kramdown — add a debug log or write to a temp file.
  2. Fix the template so its output is well-formed Kramdown/markdown: balance IAL blocks {: ... }, close all block constructs, escape raw control characters.
  3. If using block-ID substitution (the code below remaps IDs via ast.Walk), ensure generated IDs match the 20-char node-ID pattern.
  4. Reproduce by feeding the rendered markdown into a standalone Lute parse to get the exact Lute error.

Example fix

// before — template emits broken IAL
.action{.id}{: id=

// after — well-formed IAL
.action{.id}{: id="20240101000000abcdef"}
Defensive patterns

Strategy: validation

Validate before calling

// After rendering template markdown, validate it parses as Kramdown before relying on the tree
md := renderedBytes
if tree := parseKTree(md); tree == nil {
    return fmt.Errorf("rendered template output is not valid Kramdown: %s", string(md))
}

Prevention

When it happens

Trigger: A template that, after .action{} substitution, yields malformed Kramdown: unclosed block-level constructs, invalid IAL syntax ({: id=...} malformed), broken block-reference syntax, or raw HTML/structures the Lute parser rejects. The error is logged via logging.LogError and returned as errors.New(msg) with the template path 'p' interpolated.

Common situations: A template emits a partial IAL block like {: id= (missing value/close); a template concatenates block IDs incorrectly producing invalid reference syntax; a template outputs binary or non-markdown content; a template that worked under an older Lute version breaks after a Lute upgrade.

Related errors


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