siyuan-note/siyuan · error
invalid child template path
Error message
invalid child template path
What it means
resolveTemplatePackageFile validates the relative child-template path supplied inside a template package before joining it to disk. The path is rejected when it is empty, '.', absolute, '..', or escapes upward via a '../' prefix. This prevents child templates from referencing files outside the intended resolution root.
Source
Thrown at kernel/model/template_doc_tree.go:402
return true
})
if maxTemplateDocTreePlans <= count && nil != oldest {
templateDocTreePlans.Delete(oldest.id)
}
templateDocTreePlans.Store(id, plan)
templateDocTreePlansLock.Unlock()
time.AfterFunc(templateDocTreePlanTTL, func() {
templateDocTreePlans.Delete(id)
})
return collector.summary(id)
}
func resolveTemplatePackageFile(rootTemplatePath, relativePath string) (string, error) {
relativePath = strings.TrimPrefix(filepath.ToSlash(strings.TrimSpace(relativePath)), "/")
cleanPath := filepath.Clean(filepath.FromSlash(relativePath))
if "" == cleanPath || "." == cleanPath || filepath.IsAbs(cleanPath) || ".." == cleanPath ||
strings.HasPrefix(cleanPath, ".."+string(os.PathSeparator)) {
return "", errors.New("invalid child template path")
}
templatesRoot := filepath.Clean(filepath.Join(util.DataDir, "templates"))
relRootTemplate, err := filepath.Rel(templatesRoot, filepath.Clean(rootTemplatePath))
if nil != err || strings.HasPrefix(relRootTemplate, ".."+string(os.PathSeparator)) {
return "", errors.New("template path is outside templates directory")
}
parts := strings.Split(filepath.ToSlash(relRootTemplate), "/")
packageRoot := templatesRoot
if 1 < len(parts) {
packageRoot = filepath.Join(templatesRoot, parts[0])
}
absPath := filepath.Join(packageRoot, cleanPath)
if !gulu.File.IsSubPath(packageRoot, absPath) || !filelock.IsExist(absPath) {
return "", fmt.Errorf("child template [%s] not found in the current template package", relativePath)
}
realRoot, err := filepath.EvalSymlinks(packageRoot)
if nil != err {
return "", errView on GitHub (pinned to 8641553a1f)
Solutions
- Fix the child template reference in the template file to a clean relative path within the same template package (no leading '/', no '..')
- Ensure any template variable used in the child path renders to a non-empty relative path
- Replace absolute or backslash paths in the template with package-relative forward-slash paths
- If the child template lives in another package, move or copy it into the current package instead of escaping upward
Example fix
// before (in template content)
{{childTemplate "../shared/header.tpl"}}
// after
{{childTemplate "shared/header.tpl"}} Defensive patterns
Strategy: validation
Validate before calling
// Go: pre-validate child template references the same way the kernel does
p := strings.TrimPrefix(filepath.ToSlash(strings.TrimSpace(ref)), "/")
clean := filepath.Clean(filepath.FromSlash(p))
invalid := clean == "" || clean == "." || clean == ".." ||
filepath.IsAbs(clean) || strings.HasPrefix(clean, ".."+string(os.PathSeparator)) Try / catch
path, err := resolveTemplatePackageFile(root, rel)
if err != nil {
if strings.Contains(err.Error(), "invalid child template path") {
return fmt.Errorf("template references an illegal child path %q", rel)
}
return err
} Prevention
- Always write child template references as package-relative forward-slash paths
- Never start a child reference with '/' or '..'
- Guard template variables used in paths so they cannot render empty
- Lint template content for absolute or upward-traversing references before distribution
When it happens
Trigger: Calling renderTemplateDocTreeNodeContent with a template that references a child template via a relative path that is empty, absolute (e.g. '/etc/passwd' style), '..', or starts with '../'. Also triggered directly by the test TestResolveTemplateDocTreeTemplatePathRejectsEscape with crafted escape paths.
Common situations: A template author writes {{.child "../other-template"}} to reach a sibling package; an empty path results from an unrendered template variable; a Windows-authored template uses an absolute path like 'C:\templates\x.md'; a variable used for the child path resolves to blank.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- template path is outside templates directory
- child template path is outside the current template package
- path escapes templates dir: %s
- path escapes templates dir: %s
- [%s] is not an asset path
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/ae76dc3273e0c6b1.
Report an issue: GitHub.