{"record":{"id":"ec46a47b58a68522","repo":"siyuan-note/siyuan","slug":"invalid-template-path-component","errorCode":null,"errorMessage":"invalid template path component","messagePattern":"invalid template path component","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_manage.go","lineNumber":79,"sourceCode":"\t\treturn errors.New(\"invalid template path\")\n\t}\n\tfor _, part := range strings.Split(p, \"/\") {\n\t\tif strings.HasPrefix(part, \".\") {\n\t\t\treturn errors.New(\"hidden template paths are reserved\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// 新名称保持跨平台可用，已有父目录沿用原名。\nfunc validateNewTemplateName(p string) error {\n\tpart := path.Base(p)\n\tdevice := strings.ToUpper(strings.SplitN(part, \".\", 2)[0])\n\tif 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') {\n\t\treturn errors.New(\"reserved template file name\")\n\t}\n\tif strings.HasPrefix(part, \".\") || strings.TrimSpace(part) != part || strings.HasSuffix(part, \".\") || strings.ContainsAny(part, \"\\\\:<>\\\"|?*\") || strings.ContainsFunc(part, unicode.IsControl) {\n\t\treturn errors.New(\"invalid template path component\")\n\t}\n\treturn nil\n}\n\n// 清单与目录共同标识模板包，保留目录身份以维持搜索和集市更新。\nfunc isManagedTemplatePackage(root *os.Root, p string) bool {\n\t_, err := root.Lstat(path.Join(p, \"template.json\"))\n\treturn err == nil\n}\n\nfunc openTemplateRoot() (*os.Root, error) {\n\tif err := os.MkdirAll(filepath.Join(util.DataDir, \"templates\"), 0755); err != nil {\n\t\treturn nil, err\n\t}\n\treturn os.OpenRoot(filepath.Join(util.DataDir, \"templates\"))\n}\n\n// 除根目录外不接受符号链接，避免管理操作影响另一个模板包。","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_manage.go#L61-L97","documentation":"validateNewTemplateName rejects a template name component that starts with a dot, has leading/trailing whitespace, ends with a dot, contains reserved characters (\\\\ : < > \\\" | ? *), or contains control characters. The base name must be a safe, cross-platform file name component.","triggerScenarios":"ManageTemplateFiles calls validateNewTemplateName with a name like \"my template .md\", \"a<b>.md\", \"tpl?.md\", or a name with trailing spaces or embedded control characters.","commonSituations":"User-typed names copied from Windows Explorer with illegal characters; paste of strings with invisible control bytes; names with trailing periods or spaces (invalid on Windows).","solutions":["Trim whitespace and remove reserved characters (\\\\ : < > \\\" | ? *) and control chars from the name","Strip leading dots and trailing dots from the component","Sanitize names in the UI before submission"],"exampleFix":"// before\nname := \"my: report?.md\"\nerr := validateNewTemplateName(name) // fails\n// after\nname := sanitize(strings.TrimSpace(\"my: report?.md\")) // \"my report.md\"\nerr := validateNewTemplateName(name)\n","handlingStrategy":"validation","validationCode":"func validTemplateComponent(name string) bool {\n    base := path.Base(name)\n    return !strings.HasPrefix(base, \".\") && strings.TrimSpace(base) == base &&\n        !strings.HasSuffix(base, \".\") && !strings.ContainsAny(base, \"\\\\:<>\\\"|?*\") &&\n        !strings.ContainsFunc(base, unicode.IsControl)\n}","typeGuard":null,"tryCatchPattern":"if !validTemplateComponent(newName) {\n    return fmt.Errorf(\"sanitize name %q before creating template\", newName)\n}\nif err := validateNewTemplateName(newName); err != nil { return err }","preventionTips":["Trim and sanitize template names before submission","Strip reserved characters (\\\\ : < > \\\" | ? *) and control bytes","Avoid leading/trailing dots and whitespace in names"],"tags":["template","naming","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}