siyuan-note/siyuan · error
skill changed; reload it before saving, renaming or deleting
Error message
skill changed; reload it before saving, renaming or deleting
What it means
writeManagedSkillSource uses optimistic concurrency: the caller supplies a revision hash obtained from a previous read. After writing the temp file and before the atomic rename, it recomputes the revision of the on-disk skill and fails with this error if the supplied revision is empty or stale, i.e. the skill changed on disk since it was read. This prevents silently clobbering external edits.
Solutions
- Re-read the skill (action "read") to get the current content and revision, re-apply your edits, then write again with the fresh revision
- Verify which change you want to keep before overwriting the newer on-disk content
- Avoid editing skill files both in SiYuan and externally at the same time
Example fix
// before
ManageSkillFiles(SkillFileRequest{Action: "write", Path: "my-skill/SKILL.md", Content: new, Revision: oldRev})
// after
cur, _ := ManageSkillFiles(SkillFileRequest{Action: "read", Path: "my-skill/SKILL.md"})
ManageSkillFiles(SkillFileRequest{Action: "write", Path: "my-skill/SKILL.md", Content: new, Revision: cur.Revision}) Defensive patterns
Strategy: retry
Validate before calling
data, err := ManageSkillFiles(SkillFileRequest{Action: "read", Path: p})
// use data.Revision for the subsequent write Type guard
func hasRevision(req SkillFileRequest) bool { return req.Revision != "" } Try / catch
if err := ManageSkillFiles(writeReq); err != nil && strings.Contains(err.Error(), "skill changed") {
cur, rerr := ManageSkillFiles(SkillFileRequest{Action: "read", Path: p})
if rerr == nil { writeReq.Revision = cur.Revision; retry... }
} Prevention
- Always thread the Revision from read into the next write/move/remove
- Avoid editing the same skill externally while the editor has it open
- Re-read after any external modification before saving
When it happens
Trigger: ManageSkillFiles action "write" with a non-empty request.Revision (or an empty one) whose hash no longer matches managedSkillRevision of the file/directory at request.Path at replace time; also the same error raised by the pre-check in ManageSkillFiles at line 398 for write/move/remove. Any out-of-band modification between read and write triggers it.
Common situations: The user edited the skill in an external editor while the UI had it open; two browser tabs saved different versions; a sync service updated files between the client's read and write.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- agent session revision conflict
- attribute view order changed; retry the drag
- OIDC configuration changed during validation
- skill directory contains a link or special file
- source changed during asset relink
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/3d7098bac0067a0b.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill_manage.go:281
}
closeErr := file.Close()
if err != nil {
return err
}
if closeErr != nil {
return closeErr
}
if !create {
// 临时文件落盘后重新校验,避免覆盖此期间由外部编辑器保存的内容。
if err = checkManagedSkillPath(root, p); err != nil {
return err
}
currentRevision, revisionErr := managedSkillRevision(root, p)
if revisionErr != nil {
return revisionErr
}
if revision == "" || revision != currentRevision {
return errors.New("skill changed; reload it before saving, renaming or deleting")
}
if err = root.Rename(target, p); err != nil {
return err
}
}
complete = true
return nil
}
func ManageSkillFiles(request SkillFileRequest) (SkillFileData, error) {
skillManagementLock.Lock()
defer skillManagementLock.Unlock()
ret := SkillFileData{}
switch request.Action {
case "list", "read", "write", "create", "mkdir", "move", "remove":
default:
return ret, errors.New("unsupported skill operation")
}View on GitHub (pinned to 9f775e8a12)