siyuan-note/siyuan · error
rename or delete the skill directory instead of SKILL.md
Error message
rename or delete the skill directory instead of SKILL.md
What it means
SKILL.md at the top level of a skill directory is the manifest that defines the skill. It cannot be moved or deleted on its own; the whole skill directory must be renamed (move with the directory path) or removed. This keeps the manifest and directory lifecycle atomic.
Solutions
- To rename a skill, move the directory path: Action "move", Path "my-skill", Target "new-name"
- To delete a skill, remove the directory: Action "remove", Path "my-skill"
- Edit the manifest's frontmatter (name/description) with action "write" instead of moving the file
Example fix
// before
ManageSkillFiles(SkillFileRequest{Action: "remove", Path: "my-skill/SKILL.md", Revision: rev})
// after
ManageSkillFiles(SkillFileRequest{Action: "remove", Path: "my-skill", Revision: dirRev}) Defensive patterns
Strategy: validation
Validate before calling
func isManifest(p string) bool {
return strings.Count(p, "/") == 1 && strings.EqualFold(path.Base(p), "SKILL.md")
}
if (req.Action == "move" || req.Action == "remove") && isManifest(req.Path) {
// redirect to the directory path instead
} Type guard
func isSkillDirPath(p string) bool { return strings.Count(p, "/") == 0 } Try / catch
if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), "instead of SKILL.md") {
// retry against the parent directory path
} Prevention
- Never expose SKILL.md as a deletable/renamable item in UIs
- Operate on the skill directory for lifecycle changes
- Treat SKILL.md as write-only content (frontmatter edits via write)
When it happens
Trigger: ManageSkillFiles action "move" or "remove" where isManagedSkillManifest(request.Path) is true — i.e. the path has exactly one slash and its base is "SKILL.md" (case-insensitive), e.g. "my-skill/SKILL.md".
Common situations: Client UI let users select SKILL.md and issue delete/rename; scripts deleting manifest files to 'reset' a skill; refactoring code that renames manifest files to change a skill's display name.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- create a skill before creating its subdirectories
- only text files inside a skill can be edited
- unsupported skill operation
- 199
- Access to encrypted notebook data is not supported via this…
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/28da84a677194076.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/skill_manage.go:390
if ret.ReadOnlyReason == "" {
return ret, readErr
}
}
ret.Revision, err = managedSkillRevision(root, request.Path)
return ret, err
}
if info == nil || !info.IsDir() {
if !strings.Contains(request.Path, "/") {
return ret, errors.New("only text files inside a skill can be edited")
}
if info != nil {
if _, err = readManagedSkillSource(root, request.Path); err != nil {
return ret, err
}
}
}
if (request.Action == "move" || request.Action == "remove") && isManagedSkillManifest(request.Path) {
return ret, errors.New("rename or delete the skill directory instead of SKILL.md")
}
if info != nil {
revision, revisionErr := managedSkillRevision(root, request.Path)
if revisionErr != nil {
return ret, revisionErr
}
if request.Revision == "" || request.Revision != revision {
return ret, errors.New("skill changed; reload it before saving, renaming or deleting")
}
}
switch request.Action {
case "write":
if info != nil && info.IsDir() {
return ret, errors.New("cannot write a skill directory")
}
if err = writeManagedSkillSource(root, request.Path, request.Content, info == nil, request.Revision); err != nil {
return ret, err
}View on GitHub (pinned to 9f775e8a12)