{"record":{"id":"1e52c4a9345f005b","repo":"siyuan-note/siyuan","slug":"template-source-must-use-the-md-extension","errorCode":null,"errorMessage":"template source must use the .md extension","messagePattern":"template source must use the \\.md extension","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_manage.go","lineNumber":257,"sourceCode":"\tif err = checkTemplateFilePath(root, request.Path); err != nil {\n\t\treturn nil, err\n\t}\n\tabs := filepath.Join(root.Name(), filepath.FromSlash(request.Path))\n\tfilelock.Lock(abs)\n\tdefer filelock.Unlock(abs)\n\tinfo, statErr := root.Stat(request.Path)\n\tif request.Action == \"mkdir\" {\n\t\tif err = validateNewTemplateName(request.Path); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn nil, root.Mkdir(request.Path, 0755)\n\t}\n\tif statErr != nil && !(request.Action == \"write\" && request.Revision == \"\" && errors.Is(statErr, os.ErrNotExist)) {\n\t\treturn nil, statErr\n\t}\n\tif info == nil || !info.IsDir() {\n\t\tif !strings.EqualFold(path.Ext(request.Path), \".md\") {\n\t\t\treturn nil, errors.New(\"template source must use the .md extension\")\n\t\t}\n\t}\n\tif request.Action == \"read\" {\n\t\tif info.IsDir() {\n\t\t\trevision, readErr := templateFileRevision(root, request.Path)\n\t\t\treturn map[string]string{\"content\": \"\", \"revision\": revision}, readErr\n\t\t}\n\t\tcontent, readErr := readTemplateSource(root, request.Path)\n\t\treturn map[string]string{\"content\": content, \"revision\": fmt.Sprintf(\"%x\", sha256.Sum256([]byte(content))), \"path\": filepath.Join(util.DataDir, \"templates\", filepath.FromSlash(request.Path))}, readErr\n\t}\n\tif info != nil {\n\t\trevision, revisionErr := templateFileRevision(root, request.Path)\n\t\tif revisionErr != nil {\n\t\t\treturn nil, revisionErr\n\t\t}\n\t\tif request.Revision == \"\" || request.Revision != revision {\n\t\t\treturn nil, errors.New(\"template changed; reload it before saving, moving or deleting\")\n\t\t}","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_manage.go#L239-L275","documentation":"ManageTemplateFiles enforces that any non-directory template path ends with the .md extension (case-insensitive). Directories (including template packages) are exempt. This keeps the templates folder to markdown files plus package directories and prevents writing or operating on files that could not be used as templates.","triggerScenarios":"Calling ManageTemplateFiles with any action (write/move/read) where request.Path (or, for move, request.Target on a file) does not end in .md — e.g. writing \"notes.txt\", moving \"a.md\" to \"b.markdown\" or to an extensionless name, or operating on a file whose extension uses unsupported casing is fine but a wrong extension is not.","commonSituations":"A script generated template files with .txt or .mdown extensions; a move target was constructed without an extension; the client assumed the kernel would append .md automatically — it does not.","solutions":["Rename the target/path so it ends with .md, e.g. write to \"notes.md\" instead of \"notes.txt\"","For a move, append \".md\" to request.Target before calling the API","If you intended a folder, send a directory path (no extension needed) — directories are exempt from this check"],"exampleFix":"// before\n{ \"action\": \"move\", \"path\": \"a.md\", \"target\": \"notes/renamed\" }\n// after\n{ \"action\": \"move\", \"path\": \"a.md\", \"target\": \"notes/renamed.md\" }","handlingStrategy":"validation","validationCode":"function requireMd(p) {\n  if (!/\\.md$/i.test(p)) throw new Error(`template path must end in .md: ${p}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await manageTemplateFiles({ action: 'write', path: p, ... });\n} catch (e) {\n  if (String(e.message).includes('must use the .md extension')) {\n    p = p.replace(/\\.[^.]*$/, '') + '.md';  // normalize extension and retry\n  }\n}","preventionTips":["Append .md to every file path you send to the template API; the kernel never adds it for you","Only directories are exempt — keep folder paths extension-free and file paths .md","Note .markdown/.mdown are not accepted; only .md (case-insensitive)"],"tags":["template","validation","file-extension","naming"],"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"}