{"record":{"id":"145b092dd49d3370","repo":"siyuan-note/siyuan","slug":"execute-template-s-failed-s","errorCode":null,"errorMessage":"execute template [%s] failed: %s","messagePattern":"execute template \\[(.+?)\\] failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/av/calc_template.go","lineNumber":98,"sourceCode":"\n// evalRollupTemplate 使用 text/template + sprig 渲染自定义模板统计内容。\n// 返回渲染后的字符串；若该字符串可解析为数字则 isNumber 为 true 且 asNumber 为该数值。\n// 解析或执行失败时返回 err，由调用方决定如何提示用户。\nfunc evalRollupTemplate(templateContent string, ctx map[string]any) (rendered string, asNumber float64, isNumber bool, err error) {\n\tif \"\" == templateContent {\n\t\treturn\n\t}\n\n\tgoTpl := template.New(\"\").Delims(\".action{\", \"}\").Funcs(templateFuncMap())\n\ttpl, parseErr := goTpl.Parse(templateContent)\n\tif nil != parseErr {\n\t\terr = fmt.Errorf(\"parse template [%s] failed: %s\", templateContent, parseErr)\n\t\treturn\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tif execErr := tpl.Execute(buf, ctx); nil != execErr {\n\t\terr = fmt.Errorf(\"execute template [%s] failed: %s\", templateContent, execErr)\n\t\treturn\n\t}\n\n\trendered = buf.String()\n\tif \"<no value>\" == rendered {\n\t\trendered = \"\"\n\t\treturn\n\t}\n\n\t// 渲染结果若可解析为数字，则按数字处理（前端会按列数字格式显示）\n\ttrimmed := strings.TrimSpace(rendered)\n\tif \"\" != trimmed {\n\t\tif num, parseErr := strconv.ParseFloat(trimmed, 64); nil == parseErr {\n\t\t\tasNumber = num\n\t\t\tisNumber = true\n\t\t}\n\t}\n\treturn","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/av/calc_template.go#L80-L116","documentation":"Thrown by evalRollupTemplate when text/template parsed the string successfully but Execute failed against the data context. Execution failures are runtime: wrong argument types/counts to a sprig function, indexing into a missing key, or calling a removed function. The error is surfaced to the user as a 30-second toast via pushRollupTemplateErr (calc_template.go:178).","triggerScenarios":"Template references a context key that does not exist (e.g. .average instead of .avg), invokes a sprig function with the wrong arity, or calls env/expandenv/getHostByName which were deleted for security. Fires during column calc rendering in calc.go:2003 and calcFieldByTemplate in calc_template.go:164.","commonSituations":"Misremembering the exposed context keys; copy-pasting a sprig recipe that needs args differently; version skew after the security removal of env-like funcs (GHSA-v97v-gxxg-rhmq).","solutions":["Use only the documented context keys: values, strings, raw, count, sum, avg, min, max, median, nonEmptyCount (buildRollupTemplateContext, calc_template.go:35-78).","Avoid env, expandenv, getHostByName — they were removed; use static values instead.","Test arithmetic expressions in isolation (e.g. .action{ add .sum 1 }) to confirm sprig function signatures."],"exampleFix":"// before\ncalc.Template = \".action{ .average }\"\n// after\ncalc.Template = \".action{ .avg }\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Execution errors are runtime; catch them at the calc site and degrade gracefully.\nrendered, asNumber, isNumber, err := evalRollupTemplate(calc.Template, ctx)\nif err != nil {\n    pushRollupTemplateErr(err) // toast + leave calc.Result empty\n    return\n}\n// use rendered / asNumber / isNumber","preventionTips":["Keep a whitelist of context keys you reference (sum/avg/min/max/median/count/nonEmptyCount/values/strings/raw).","Avoid env/expandenv/getHostByName — they are removed for security (GHSA-v97v-gxxg-rhmq).","Add a dry-run render in the UI with sample data so users see execution errors before saving."],"tags":["template","av","rollup","calc","sprig","runtime"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}