{"record":{"id":"5e9248975e5a82b8","repo":"siyuan-note/siyuan","slug":"path-is-required-template","errorCode":null,"errorMessage":"path is required","messagePattern":"path is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_path.go","lineNumber":16,"sourceCode":"package model\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\n// openTemplatePath 将绝对或相对路径限制在模板根目录内，文件操作通过根目录句柄防止符号链接越界。\nfunc openTemplatePath(p string) (*os.Root, string, error) {\n\tif p == \"\" {\n\t\treturn nil, \"\", errors.New(\"path is required\")\n\t}\n\tbase, err := filepath.Abs(filepath.Join(util.DataDir, \"templates\"))\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tabs := p\n\tif !filepath.IsAbs(abs) {\n\t\tabs = filepath.Join(base, p)\n\t}\n\trel, err := filepath.Rel(base, abs)\n\tif err != nil || rel == \".\" || rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) {\n\t\treturn nil, \"\", errors.New(\"template path is outside templates directory\")\n\t}\n\troot, err := os.OpenRoot(base)\n\treturn root, rel, err\n}\n\n// ReadTemplateFile 在模板根目录内读取普通文件，禁止通过符号链接读取目录外的数据。","sourceCodeStart":1,"sourceCodeEnd":34,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/template_path.go#L1-L34","documentation":"openTemplatePath resolves a user-supplied template path against the workspace templates root and requires a non-empty input. An empty string cannot be anchored relative to the templates directory, so the helper fails fast with this error instead of resolving to the root itself or an unpredictable location. It is used by ReadTemplateFile and RemoveTemplate.","triggerScenarios":"Calling ReadTemplateFile(\"\") or RemoveTemplate(\"\") — i.e. passing an empty path string; also occurs when an upstream variable that should hold a template path was never populated.","commonSituations":"A config field or document attribute holding the template path is empty; a caller forgot to fill the path after building a request; UI state lost the selection and passed an empty string downstream.","solutions":["Ensure the path argument is populated before calling; check for emptiness and return a friendly message to the user","If the caller is config-driven, validate the template-path config field at load time","Provide a default template path or prompt the user to select a template when the value is empty","Fix upstream logic so the path variable is assigned before the call"],"exampleFix":"// before\ncontent, err := model.ReadTemplateFile(cfg.TemplatePath)\n// after\nif cfg.TemplatePath == \"\" { return errors.New(\"no template configured\") }\ncontent, err := model.ReadTemplateFile(cfg.TemplatePath)","handlingStrategy":"validation","validationCode":"if p == \"\" { return errors.New(\"template path must be set\") }","typeGuard":"func hasTemplatePath(p string) bool { return strings.TrimSpace(p) != \"\" }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"path is required\") {\n    // prompt user to choose a template\n}","preventionTips":["Validate config fields holding template paths at startup","Never pass through an unset variable; default or prompt instead","Check UI selection state before invoking template APIs"],"tags":["validation","missing-argument","templates"],"backgroundTag":"empty-required-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}