{"record":{"id":"fae3430a8ccb58d6","repo":"siyuan-note/siyuan","slug":"only-text-files-inside-a-skill-can-be-edited","errorCode":null,"errorMessage":"only text files inside a skill can be edited","messagePattern":"only text files inside a skill can be edited","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill_manage.go","lineNumber":381,"sourceCode":"\tif request.Action == \"read\" {\n\t\tif !info.IsDir() && strings.Contains(request.Path, \"/\") {\n\t\t\tcontent, readErr := readManagedSkillSource(root, request.Path)\n\t\t\tif readErr == nil {\n\t\t\t\tret.Content = &content\n\t\t\t\tret.Revision = fmt.Sprintf(\"%x\", sha256.Sum256([]byte(content)))\n\t\t\t\treturn ret, nil\n\t\t\t}\n\t\t\tret.ReadOnlyReason = skillSourceReadOnlyReason(readErr)\n\t\t\tif ret.ReadOnlyReason == \"\" {\n\t\t\t\treturn ret, readErr\n\t\t\t}\n\t\t}\n\t\tret.Revision, err = managedSkillRevision(root, request.Path)\n\t\treturn ret, err\n\t}\n\tif info == nil || !info.IsDir() {\n\t\tif !strings.Contains(request.Path, \"/\") {\n\t\t\treturn ret, errors.New(\"only text files inside a skill can be edited\")\n\t\t}\n\t\tif info != nil {\n\t\t\tif _, err = readManagedSkillSource(root, request.Path); err != nil {\n\t\t\t\treturn ret, err\n\t\t\t}\n\t\t}\n\t}\n\tif (request.Action == \"move\" || request.Action == \"remove\") && isManagedSkillManifest(request.Path) {\n\t\treturn ret, errors.New(\"rename or delete the skill directory instead of SKILL.md\")\n\t}\n\tif info != nil {\n\t\trevision, revisionErr := managedSkillRevision(root, request.Path)\n\t\tif revisionErr != nil {\n\t\t\treturn ret, revisionErr\n\t\t}\n\t\tif request.Revision == \"\" || request.Revision != revision {\n\t\t\treturn ret, errors.New(\"skill changed; reload it before saving, renaming or deleting\")\n\t\t}","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill_manage.go#L363-L399","documentation":"Only text files nested inside a skill directory (path containing \"/\") may be edited via write/move/remove. Top-level entries under the skills root must be skill directories; attempting to edit a bare top-level file raises this error. It keeps the invariant that the skills root contains only skill directories plus a SKILL.md manifest each.","triggerScenarios":"ManageSkillFiles action write/move/remove where request.Path has no \"/\" (e.g. \"notes.txt\") and Lstat shows it is not a directory (or is nil): a top-level loose file at the skills root.","commonSituations":"A stray file was dropped into data/storage/ai/agent/skills and the client tried to edit it like a skill resource; client code did not enforce the skill/<file> path shape.","solutions":["Move the file into an existing skill directory and edit it there as \"<skill>/<file>\"","Create a skill first and place the file inside it","Remove the stray top-level file if it does not belong in skill storage"],"exampleFix":"// before\nManageSkillFiles(SkillFileRequest{Action: \"write\", Path: \"notes.txt\", Content: txt, Revision: rev})\n// after\nManageSkillFiles(SkillFileRequest{Action: \"write\", Path: \"my-skill/notes.txt\", Content: txt, Revision: rev})","handlingStrategy":"validation","validationCode":"if !strings.Contains(req.Path, \"/\") && req.Action != \"list\" && req.Action != \"create\" {\n  return fmt.Errorf(\"top-level entries are skill directories; edit files as <skill>/<file>\")\n}","typeGuard":"func isEditableSkillFilePath(p string) bool { return strings.Contains(p, \"/\") }","tryCatchPattern":"if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), \"only text files inside a skill\") {\n  // relocate the file into a skill directory\n}","preventionTips":["Only issue edit verbs for paths of the form <skill>/<file>","Keep loose files out of the skills root","Render top-level entries as non-editable in UIs"],"tags":["api","invalid-path","skills"],"backgroundTag":"invalid-argument-format","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"}