{"record":{"id":"a94091d754f41528","repo":"siyuan-note/siyuan","slug":"cannot-write-a-template-directory","errorCode":null,"errorMessage":"cannot write a template directory","messagePattern":"cannot write a template directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/template_manage.go","lineNumber":288,"sourceCode":"\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}\n\t}\n\tswitch request.Action {\n\tcase \"write\":\n\t\tif info == nil {\n\t\t\tif err = validateNewTemplateName(request.Path); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif len(request.Content) > maxTemplateSourceSize {\n\t\t\treturn nil, errors.New(\"template source is too large\")\n\t\t}\n\t\tif info != nil && info.IsDir() {\n\t\t\treturn nil, errors.New(\"cannot write a template directory\")\n\t\t}\n\t\terr = writeTemplateSource(root, request.Path, request.Content, info == nil)\n\t\treturn map[string]string{\"revision\": fmt.Sprintf(\"%x\", sha256.Sum256([]byte(request.Content)))}, err\n\tcase \"move\":\n\t\tif info.IsDir() && isManagedTemplatePackage(root, request.Path) {\n\t\t\treturn nil, errors.New(\"template packages cannot be renamed or moved\")\n\t\t}\n\t\tif err = checkTemplateFilePath(root, request.Target); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err = validateNewTemplateName(request.Target); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !info.IsDir() && !strings.EqualFold(path.Ext(request.Target), \".md\") {\n\t\t\treturn nil, errors.New(\"template source must use the .md extension\")\n\t\t}\n\t\tif _, err = root.Stat(request.Target); !errors.Is(err, os.ErrNotExist) {\n\t\t\treturn nil, errors.New(\"template destination already exists or is inaccessible\")","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/template_manage.go#L270-L306","documentation":"The write action only accepts regular files. If the request.Path exists and is a directory (a plain template folder or a template package with template.json), the write is rejected because a directory cannot be replaced by file content — doing so would destroy package resources. This error indicates you aimed a file-write at a directory path.","triggerScenarios":"ManageTemplateFiles with action=\"write\" where request.Path already exists as a directory — e.g. writing to \"my-pack\" when data/templates/my-pack/ is a template package, or a path collision where a directory was created earlier with action=\"mkdir\" under the same name.","commonSituations":"A name collision: a template package directory already occupies the name the client wants for a new .md file; the client built the path without the .md suffix so it accidentally matched a directory; automation created directories and files inconsistently.","solutions":["Write to a different filename with a .md extension that does not collide with an existing directory","If you intended to update a file inside the package, target the inner file, e.g. \"my-pack/template.md\"","Remove or rename the existing directory first if it is truly unwanted (action=\"remove\" or action=\"move\")"],"exampleFix":"// before\n{ \"action\": \"write\", \"path\": \"my-pack\", \"revision\": rev, \"content\": \"text\" }   // my-pack/ is a package dir\n// after\n{ \"action\": \"write\", \"path\": \"my-pack/template.md\", \"revision\": innerRev, \"content\": \"text\" }","handlingStrategy":"validation","validationCode":"const entry = (await manageTemplateFiles({ action: 'list' })).find(e => e.path === p);\nif (entry?.isDir) {\n  throw new Error(`'${p}' is a directory; write to a file inside it instead`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await manageTemplateFiles({ action: 'write', path: p, revision: rev, content });\n} catch (e) {\n  if (String(e.message).includes('cannot write a template directory')) {\n    // redirect the write to p + '/template.md' or another non-colliding name\n  }\n}","preventionTips":["Consult the list action's isDir/isPackage flags before writing","Always include the .md filename as the last path segment for file writes","Watch for name collisions between template files and package directories"],"tags":["template","path-type","validation","directory"],"backgroundTag":"path-is-not-a-directory","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"}