siyuan-note/siyuan · warning

parse template [%s] failed: %s

Error message

parse template [%s] failed: %s

What it means

Thrown by evalRollupTemplate when Go's text/template cannot parse the user-supplied calc/rollup 'Template' operator string. SiYuan uses custom delimiters '.action{' and '}' (not the usual {{ }}) on top of sprig plus a custom countif function. A parse error means the template grammar itself is malformed, independent of any data.

Source

Thrown at kernel/av/calc_template.go:92

		ctx["min"] = float64(0)
		ctx["max"] = float64(0)
		ctx["median"] = float64(0)
	}
	return ctx
}

// evalRollupTemplate 使用 text/template + sprig 渲染自定义模板统计内容。
// 返回渲染后的字符串;若该字符串可解析为数字则 isNumber 为 true 且 asNumber 为该数值。
// 解析或执行失败时返回 err,由调用方决定如何提示用户。
func evalRollupTemplate(templateContent string, ctx map[string]any) (rendered string, asNumber float64, isNumber bool, err error) {
	if "" == templateContent {
		return
	}

	goTpl := template.New("").Delims(".action{", "}").Funcs(templateFuncMap())
	tpl, parseErr := goTpl.Parse(templateContent)
	if nil != parseErr {
		err = fmt.Errorf("parse template [%s] failed: %s", templateContent, parseErr)
		return
	}

	buf := &bytes.Buffer{}
	if execErr := tpl.Execute(buf, ctx); nil != execErr {
		err = fmt.Errorf("execute template [%s] failed: %s", templateContent, execErr)
		return
	}

	rendered = buf.String()
	if "<no value>" == rendered {
		rendered = ""
		return
	}

	// 渲染结果若可解析为数字,则按数字处理(前端会按列数字格式显示)
	trimmed := strings.TrimSpace(rendered)
	if "" != trimmed {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Replace {{ / }} delimiters with .action{ / } (the kernel's custom delimiters at calc_template.go:89).
  2. Strip the template down to a literal plus one field reference (e.g. .action{ .sum }) and re-add complexity until it breaks to isolate the syntax error.
  3. Cross-check function names against sprig's documentation and the custom countif; note env/expandenv/getHostByName are deliberately removed for security (calc_template.go:187-189).

Example fix

// before
config.Template = "{{ .sum }} / {{ .count }}"
// after
config.Template = ".action{ .sum } / .action{ .count }"
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate a user-entered template before saving it on the calc.
func validateCalcTemplate(tpl string) error {
    if tpl == "" {
        return nil
    }
    goTpl := template.New("").Delims(".action{", "}").Funcs(template.FuncMap{
        // surface the same funcs the kernel uses, or stub them
    })
    if _, err := goTpl.Parse(tpl); err != nil {
        return err
    }
    return nil
}

Try / catch

// The kernel already toasts parse errors via pushRollupTemplateErr.
// In your own calc driver, mirror that handling:
if _, _, _, err := evalRollupTemplate(tpl, ctx); err != nil {
    logging.LogErrorf("rollup template failed: %s", err)
    util.PushErrMsg(err.Error(), 30000)
    return // leave calc.Result empty
}

Prevention

When it happens

Trigger: User selects the 'Template' calculation operator on an Attribute View column and enters a string with unclosed actions, bad pipelines, or a typo'd function name. Also fires when someone uses standard {{ }} delimiters out of habit, since SiYuan's parser only recognizes .action{ }.

Common situations: Writing '.action{{ .sum }}' instead of '.action{ .sum }'; unbalanced parentheses; calling an undefined sprig function; copying a Helm/Go template verbatim that uses different delimiters.

Related errors


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