{"record":{"id":"e5d8b4b26206c5bb","repo":"siyuan-note/siyuan","slug":"create-a-skill-before-creating-its-subdirectories","errorCode":null,"errorMessage":"create a skill before creating its subdirectories","messagePattern":"create a skill before creating its subdirectories","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill_manage.go","lineNumber":356,"sourceCode":"\t\t}\n\t\ttmp := \".skill-\" + ast.NewNodeID()\n\t\tif err = root.Mkdir(tmp, 0755); err != nil {\n\t\t\treturn ret, err\n\t\t}\n\t\tdefer root.RemoveAll(tmp)\n\t\tif err = writeManagedSkillSource(root, path.Join(tmp, \"SKILL.md\"), content, true, \"\"); err != nil {\n\t\t\treturn ret, err\n\t\t}\n\t\tif err = root.Rename(tmp, request.Path); err != nil {\n\t\t\treturn ret, err\n\t\t}\n\t\tret.Content = &content\n\t\tret.Revision = fmt.Sprintf(\"%x\", sha256.Sum256([]byte(content)))\n\t\treturn ret, nil\n\t}\n\tif request.Action == \"mkdir\" {\n\t\tif !strings.Contains(request.Path, \"/\") {\n\t\t\treturn ret, errors.New(\"create a skill before creating its subdirectories\")\n\t\t}\n\t\treturn ret, root.Mkdir(request.Path, 0755)\n\t}\n\tif statErr != nil && !(request.Action == \"write\" && request.Revision == \"\" && errors.Is(statErr, os.ErrNotExist)) {\n\t\treturn ret, statErr\n\t}\n\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}","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill_manage.go#L338-L374","documentation":"The \"mkdir\" action only creates subdirectories inside an existing skill; a path without \"/\" would be a top-level skill name. This error tells the caller that skills themselves must be created with the \"create\" action before their subdirectories can be made.","triggerScenarios":"ManageSkillFiles{Action:\"mkdir\"} with a slash-free request.Path such as \"my-skill\" instead of \"my-skill/subdir\"; trying to create a new skill directory via mkdir.","commonSituations":"Confusing \"create\" (new skill + SKILL.md) with \"mkdir\" (subdirectory); generic file-management code calling mkdir for every directory level including the top level.","solutions":["Create the skill first with Action \"create\", then call mkdir with a path that includes the skill prefix","Verify request.Path contains at least one \"/\" before calling mkdir"],"exampleFix":"// before\nManageSkillFiles(SkillFileRequest{Action: \"mkdir\", Path: \"my-skill\"})\n// after\nManageSkillFiles(SkillFileRequest{Action: \"create\", Path: \"my-skill\"})\nManageSkillFiles(SkillFileRequest{Action: \"mkdir\", Path: \"my-skill/scripts\"})","handlingStrategy":"validation","validationCode":"if !strings.Contains(req.Path, \"/\") {\n  // create the skill first with Action \"create\", then mkdir \"<skill>/<sub>\"\n}","typeGuard":"func isSkillSubpath(p string) bool { return strings.Contains(p, \"/\") }","tryCatchPattern":"if err := ManageSkillFiles(req); err != nil && strings.Contains(err.Error(), \"create a skill before creating\") {\n  // create the skill, then retry mkdir\n}","preventionTips":["Ensure the parent skill exists before any mkdir call","Always build subdirectory paths as \"<skill>/<sub>\"","Distinguish create (skill) from mkdir (subdirectory) in client code"],"tags":["api","invalid-argument","skills"],"backgroundTag":"invalid-state-transition","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"}