siyuan-note/siyuan · error
cannot write a template directory
Error message
cannot write a template directory
What it means
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.
Source
Thrown at kernel/model/template_manage.go:288
if revisionErr != nil {
return nil, revisionErr
}
if request.Revision == "" || request.Revision != revision {
return nil, errors.New("template changed; reload it before saving, moving or deleting")
}
}
switch request.Action {
case "write":
if info == nil {
if err = validateNewTemplateName(request.Path); err != nil {
return nil, err
}
}
if len(request.Content) > maxTemplateSourceSize {
return nil, errors.New("template source is too large")
}
if info != nil && info.IsDir() {
return nil, errors.New("cannot write a template directory")
}
err = writeTemplateSource(root, request.Path, request.Content, info == nil)
return map[string]string{"revision": fmt.Sprintf("%x", sha256.Sum256([]byte(request.Content)))}, err
case "move":
if info.IsDir() && isManagedTemplatePackage(root, request.Path) {
return nil, errors.New("template packages cannot be renamed or moved")
}
if err = checkTemplateFilePath(root, request.Target); err != nil {
return nil, err
}
if err = validateNewTemplateName(request.Target); err != nil {
return nil, err
}
if !info.IsDir() && !strings.EqualFold(path.Ext(request.Target), ".md") {
return nil, errors.New("template source must use the .md extension")
}
if _, err = root.Stat(request.Target); !errors.Is(err, os.ErrNotExist) {
return nil, errors.New("template destination already exists or is inaccessible")View on GitHub (pinned to 8641553a1f)
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")
Example fix
// before
{ "action": "write", "path": "my-pack", "revision": rev, "content": "text" } // my-pack/ is a package dir
// after
{ "action": "write", "path": "my-pack/template.md", "revision": innerRev, "content": "text" } Defensive patterns
Strategy: validation
Validate before calling
const entry = (await manageTemplateFiles({ action: 'list' })).find(e => e.path === p);
if (entry?.isDir) {
throw new Error(`'${p}' is a directory; write to a file inside it instead`);
} Try / catch
try {
await manageTemplateFiles({ action: 'write', path: p, revision: rev, content });
} catch (e) {
if (String(e.message).includes('cannot write a template directory')) {
// redirect the write to p + '/template.md' or another non-colliding name
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- path is required
- attribute view [%s] has no available visible view
- invalid content template path
- createDocTree exceeds the maximum depth of %d
- createDocTree document list must not be empty
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/a94091d754f41528.
Report an issue: GitHub.