siyuan-note/siyuan · error
template changed; reload it before saving, moving or deletin
Error message
template changed; reload it before saving, moving or deleting
What it means
ManageTemplateFiles implements optimistic concurrency: for any existing path (info != nil) it recomputes a SHA-256 revision of the file/directory and requires the caller to pass that exact value in request.Revision before allowing write, move, or remove. If the revision is missing or stale — meaning someone else modified the template since the caller last read it — the operation is rejected so conflicting edits do not silently overwrite each other.
Source
Thrown at kernel/model/template_manage.go:274
if !strings.EqualFold(path.Ext(request.Path), ".md") {
return nil, errors.New("template source must use the .md extension")
}
}
if request.Action == "read" {
if info.IsDir() {
revision, readErr := templateFileRevision(root, request.Path)
return map[string]string{"content": "", "revision": revision}, readErr
}
content, readErr := readTemplateSource(root, request.Path)
return map[string]string{"content": content, "revision": fmt.Sprintf("%x", sha256.Sum256([]byte(content))), "path": filepath.Join(util.DataDir, "templates", filepath.FromSlash(request.Path))}, readErr
}
if info != nil {
revision, revisionErr := templateFileRevision(root, request.Path)
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":View on GitHub (pinned to 8641553a1f)
Solutions
- Re-read the template (action="read") to get the fresh revision and your desired content, then retry the write/move/remove with the new Revision
- If your change should win, merge your edits into the freshly read content before re-saving — do not blindly retry with the old content
- Check what else is touching data/templates (sync clients, other SiYuan windows) and avoid concurrent edits
Example fix
// before (stale revision)
{ "action": "write", "path": "t.md", "revision": "abc123...", "content": "new text" }
// after: first
{ "action": "read", "path": "t.md" } -> revision "def456..."
// then
{ "action": "write", "path": "t.md", "revision": "def456...", "content": "merged text" } Defensive patterns
Strategy: retry
Validate before calling
// always carry the revision from your last read into any mutating call
const { revision } = await manageTemplateFiles({ action: 'read', path: p });
await manageTemplateFiles({ action: 'write', path: p, revision, content }); Try / catch
try {
await saveTemplate(p, content, cachedRev);
} catch (e) {
if (String(e.message).includes('reload it before')) {
const fresh = await manageTemplateFiles({ action: 'read', path: p });
const merged = mergeEdits(fresh.content, content); // never blindly overwrite
await saveTemplate(p, merged, fresh.revision);
}
} Prevention
- Never cache a revision across user think-time; re-read right before saving
- Avoid editing the same template in multiple windows/tabs
- Pause or scope sync clients while performing bulk template operations
- Treat this error as a merge-conflict signal, not a transient failure — re-read and merge
When it happens
Trigger: Any write/move/remove on an existing template where request.Revision is empty, or differs from the current hash: another window/tab saved the template in between, a sync service touched the file (mtime/size change), the caller cached a revision from before an external edit, or a directory's contents changed (any entry added/removed/renamed changes the directory revision).
Common situations: Two editor tabs editing the same template; a WebDAV/cloud sync updates the file between read and save; a long-lived client holds a revision across an external re-save; the user edited the file on disk while the app had it open.
Related errors
- agent runtime turn changed
- template already exists, use --overwrite to replace: %s
- the document used to render the template has changed
- template document tree plan contains an invalid document sna
- Related operations are being processed, please try again lat
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/1ec43a09f141161e.
Report an issue: GitHub.