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

  1. 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
  2. Fix or remove malformed kramdown block IAL remnants ({: ...}) and unclosed code fences/table rows in the template
  3. Ensure the template renders to at least one valid block (heading, paragraph, list) and is not empty for the given block's data model
  4. 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

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


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)