siyuan-note/siyuan · error

hidden template paths are reserved

Error message

hidden template paths are reserved

What it means

Any path segment of the template relative path starting with a dot is rejected as reserved for hidden entries. Hidden files/directories are excluded from the template namespace to avoid colliding with metadata or dotfiles on any platform.

Source

Thrown at kernel/model/template_manage.go:65

}

type TemplateFileEntry struct {
	Path      string `json:"path"`
	IsDir     bool   `json:"isDir"`
	IsPackage bool   `json:"isPackage,omitempty"`
}

// 访问已有模板只校验目录边界,不对文件名进行清理或改写。
func validateTemplateRelativePath(p string, allowRoot bool) error {
	if p == "" && allowRoot {
		return nil
	}
	if p == "" || !fs.ValidPath(p) || strings.ContainsAny(p, "\\:\x00") {
		return errors.New("invalid template path")
	}
	for _, part := range strings.Split(p, "/") {
		if strings.HasPrefix(part, ".") {
			return errors.New("hidden template paths are reserved")
		}
	}
	return nil
}

// 新名称保持跨平台可用,已有父目录沿用原名。
func validateNewTemplateName(p string) error {
	part := path.Base(p)
	device := strings.ToUpper(strings.SplitN(part, ".", 2)[0])
	if device == "CON" || device == "PRN" || device == "AUX" || device == "NUL" || (len(device) == 4 && (strings.HasPrefix(device, "COM") || strings.HasPrefix(device, "LPT")) && device[3] >= '1' && device[3] <= '9') {
		return errors.New("reserved template file name")
	}
	if strings.HasPrefix(part, ".") || strings.TrimSpace(part) != part || strings.HasSuffix(part, ".") || strings.ContainsAny(part, "\\:<>\"|?*") || strings.ContainsFunc(part, unicode.IsControl) {
		return errors.New("invalid template path component")
	}
	return nil
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Remove or rename the dot-prefixed segment so no path part starts with "."
  2. Store templates in normal (non-hidden) directories
  3. Filter out dotfiles before iterating template candidates

Example fix

// before
err := validateTemplateRelativePath(".hidden/tpl.md", false)
// after
err := validateTemplateRelativePath("templates/tpl.md", false)
Defensive patterns

Strategy: validation

Validate before calling

func hasHiddenSegment(p string) bool {
    for _, part := range strings.Split(p, "/") {
        if strings.HasPrefix(part, ".") { return true }
    }
    return false
}
// reject before calling: hasHiddenSegment(p)

Try / catch

if hasHiddenSegment(p) {
    return errors.New("template paths must not contain hidden (dot-prefixed) segments")
}
if err := validateTemplateRelativePath(p, false); err != nil { return err }

Prevention

When it happens

Trigger: validateTemplateRelativePath (via DocSaveAsTemplateInDirectory or checkTemplateFilePath) receives a path where strings.HasPrefix(part, ".") for some segment, e.g. ".config/tpl.md" or "notes/.hidden.md".

Common situations: Users trying to store templates in a dot-directory; hidden temp files (e.g. ".DS_Store", editor swap files) being passed as template paths; programmatic paths built from hidden config dirs.

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/b57032e36b49f06d. Report an issue: GitHub.