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

  1. Pass only the template's path relative to <data>/templates/, e.g. "basic.md" or "sub/tpl.md".
  2. Strip any leading slash or workspace prefix from the path before calling; use the path exactly as listed under Templates in the UI.
  3. 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

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


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)