siyuan-note/siyuan · error
parse tree [ ] failed
Error message
parse tree [%s] failed
What it means
Thrown by model.RenderTemplate (kernel/model/template.go:374) after a template file is read, executed as a Go text/template with delims .action{ } against the block's data model, and the rendered bytes are handed to parseKTree (Lute kramdown). If Lute returns a nil tree, the rendered output is not valid SiYuan kramdown markdown, and the error embeds the template file path p. Note this is distinct from the Conf.Language(44) errors raised one step earlier for template parse/execute failures - this error means execution succeeded but the output is unparseable.
Solutions
- Open the template file at the path shown in the error and inspect what it renders to, bisecting by temporarily replacing .action{...} constructs with plain text
- Fix or remove malformed kramdown block IAL remnants ({: ...}) and unclosed code fences/table rows in the template
- Ensure the template renders to at least one valid block (heading, paragraph, list) and is not empty for the given block's data model
- Dry-run the same template via /api/template/render on a known-good block id until it parses, then reuse it for doc creation
Example fix
// data/templates/report.md (before) - emits a broken, unclosed IAL
.action{.title}{: id="broken
// after - plain valid kramdown
# .action{.title} Defensive patterns
Strategy: try-catch
Validate before calling
// Dry-run the template on a known-good block before relying on it:
// POST /api/template/render {"id": "<known block id>", "path": "data/templates/my.md"}
// A 200 response with a DOM means the rendered output parses; only then use it for doc creation. Try / catch
// Go caller
if _, _, err := model.RenderTemplate(p, id, false); err != nil {
if strings.Contains(err.Error(), "parse tree [") {
// template content problem - fix the template file at the reported path
}
} Prevention
- Keep templates as plain kramdown markdown; avoid emitting raw IAL syntax ({: ...}) from .action{} constructs
- Re-run /api/template/render after every template edit before using it in automation
- Ensure templates always render at least one valid block for any data model
When it happens
Trigger: Calling /api/template/render (or creating/updating a doc whose template renders through RenderTemplate) with a template body that expands to text Lute cannot parse: unclosed code fences, stray or malformed kramdown IAL syntax like {: id=..., .action{...} constructs that emit raw HTML or garbage, or a template that renders to empty/whitespace-only output.
Common situations: Hand-edited templates in data/templates; templates copied from the web containing non-kramdown syntax; SQL template funcs (sql.SQLTemplateFuncs) emitting unexpected values; a loop/condition that swallows all content leaving nothing parseable.
Related errors
- parse template tree failed
- 347
- empty result
- HTML to Markdown conversion failed
- invalid template document attributes
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/e3251d03e7ec6e6b.
Report an issue: GitHub.
Appendix: 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 afa823b6b4)