siyuan-note/siyuan · error
invalid content template path
Error message
invalid content template path
What it means
resolveDocContentTemplatePath validates a template path supplied for applying a document content template. It rejects paths that are empty, resolve to '.', are absolute, or escape the templates root via '..' components. This is a path-safety guard ensuring only relative paths inside <data>/templates/ can be used.
Solutions
- Pass only the template's path relative to <data>/templates/, e.g. "basic.md" or "sub/tpl.md".
- Strip any leading slash or workspace prefix from the path before calling; use the path exactly as listed under Templates in the UI.
- If the caller received the path from user input, reject/trim empty and absolute values before invoking the API.
Example fix
// before applyDocContentTemplate(boxID, "/home/user/data/templates/tpl.md", ...) // after applyDocContentTemplate(boxID, "tpl.md", ...)
Defensive patterns
Strategy: validation
Validate before calling
function isValidTemplatePath(p) {
if (typeof p !== "string") return false;
let t = p.trim().replace(/^\//, "");
if (!t || t === "." || t === "..") return false;
return !/^(\/|[A-Za-z]:[\\/])/.test(p) && !t.split("/").includes("..");
}
if (!isValidTemplatePath(templatePath)) throw new Error("invalid template path"); Prevention
- Always store and pass template paths relative to the workspace templates directory
- Never construct template paths from absolute filesystem locations
- Normalize user input with path normalization before sending to the API
When it happens
Trigger: Calling applyDocContentTemplate (e.g. via the templates API) with a templatePath that is "", ".", an absolute path like "/etc/passwd" or "C:/tmp/t.md", or a path starting with "../" after trimming a leading slash.
Common situations: Passing a full filesystem path instead of the template's name-relative path; concatenating user home dirs into the path; a client sending an empty template field when the user did not pick a template.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- hidden template paths are reserved
- invalid history source path
- invalid skill path
- invalid skill path component
- invalid template path
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/ec0b35c6326aa3ab.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/template.go:1220
if "name" == key || "alias" == key || "bookmark" == key || "memo" == key || "icon" == key ||
strings.HasPrefix(key, "custom-") {
tree.Root.SetIALAttr(key, value)
}
}
tree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
if err = indexWriteTreeUpsertQueue(tree); nil != err {
return err
}
av.BatchUpsertBlockRel(tree.Root.ChildrenByType(ast.NodeAttributeView))
return nil
}
func resolveDocContentTemplatePath(templatePath string) (string, error) {
templatePath = strings.TrimPrefix(filepath.ToSlash(strings.TrimSpace(templatePath)), "/")
cleanPath := filepath.Clean(filepath.FromSlash(templatePath))
if "" == cleanPath || "." == cleanPath || filepath.IsAbs(cleanPath) || ".." == cleanPath ||
strings.HasPrefix(cleanPath, ".."+string(os.PathSeparator)) {
return "", errors.New("invalid content template path")
}
templateRoot := filepath.Join(util.DataDir, "templates")
absPath := filepath.Join(templateRoot, cleanPath)
if !gulu.File.IsSubPath(templateRoot, absPath) {
return "", errors.New("content template path is outside templates directory")
}
if !filelock.IsExist(absPath) {
return "", fmt.Errorf("content template [%s] not found", templatePath)
}
realRoot, err := filepath.EvalSymlinks(templateRoot)
if nil != err {
return "", err
}
realPath, err := filepath.EvalSymlinks(absPath)
if nil != err {
return "", err
}
info, err := os.Stat(realPath)View on GitHub (pinned to 9f775e8a12)