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
- 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.
- Fix the template so its output is well-formed Kramdown/markdown: balance IAL blocks {: ... }, close all block constructs, escape raw control characters.
- If using block-ID substitution (the code below remaps IDs via ast.Walk), ensure generated IDs match the 20-char node-ID pattern.
- 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
- Inspect rendered markdown output (buf.Bytes()) when debugging — the failure is in the OUTPUT, not the template syntax.
- Ensure IAL blocks {: id="..."} are well-formed with quoted values.
- Avoid emitting raw control characters or partial block constructs from templates.
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
- Conf.Language(44) {err.Error()}
- HTML to Markdown conversion failed: %s
- HTML to Markdown panicked: %v
- HTML to text panicked: %v
- parse template [%s] failed: %s
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/e3251d03e7ec6e6b.
Report an issue: GitHub.