{"record":{"id":"3d7098bac0067a0b","repo":"siyuan-note/siyuan","slug":"skill-changed-reload-it-before-saving-renaming-or-deleting","errorCode":null,"errorMessage":"skill changed; reload it before saving, renaming or deleting","messagePattern":"skill changed; reload it before saving, renaming or deleting","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill_manage.go","lineNumber":281,"sourceCode":"\t}\n\tcloseErr := file.Close()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif closeErr != nil {\n\t\treturn closeErr\n\t}\n\tif !create {\n\t\t// 临时文件落盘后重新校验，避免覆盖此期间由外部编辑器保存的内容。\n\t\tif err = checkManagedSkillPath(root, p); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcurrentRevision, revisionErr := managedSkillRevision(root, p)\n\t\tif revisionErr != nil {\n\t\t\treturn revisionErr\n\t\t}\n\t\tif revision == \"\" || revision != currentRevision {\n\t\t\treturn errors.New(\"skill changed; reload it before saving, renaming or deleting\")\n\t\t}\n\t\tif err = root.Rename(target, p); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcomplete = true\n\treturn nil\n}\n\nfunc ManageSkillFiles(request SkillFileRequest) (SkillFileData, error) {\n\tskillManagementLock.Lock()\n\tdefer skillManagementLock.Unlock()\n\tret := SkillFileData{}\n\tswitch request.Action {\n\tcase \"list\", \"read\", \"write\", \"create\", \"mkdir\", \"move\", \"remove\":\n\tdefault:\n\t\treturn ret, errors.New(\"unsupported skill operation\")\n\t}","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill_manage.go#L263-L299","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nManageSkillFiles(SkillFileRequest{Action: \"write\", Path: \"my-skill/SKILL.md\", Content: new, Revision: oldRev})\n// after\ncur, _ := ManageSkillFiles(SkillFileRequest{Action: \"read\", Path: \"my-skill/SKILL.md\"})\nManageSkillFiles(SkillFileRequest{Action: \"write\", Path: \"my-skill/SKILL.md\", Content: new, Revision: cur.Revision})","handlingStrategy":"retry","validationCode":"data, err := ManageSkillFiles(SkillFileRequest{Action: \"read\", Path: p})\n// use data.Revision for the subsequent write","typeGuard":"func hasRevision(req SkillFileRequest) bool { return req.Revision != \"\" }","tryCatchPattern":"if err := ManageSkillFiles(writeReq); err != nil && strings.Contains(err.Error(), \"skill changed\") {\n  cur, rerr := ManageSkillFiles(SkillFileRequest{Action: \"read\", Path: p})\n  if rerr == nil { writeReq.Revision = cur.Revision; retry... }\n}","preventionTips":["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"],"tags":["concurrency","optimistic-locking","revision-conflict","skills"],"backgroundTag":"conflicting-config-options","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}