{"record":{"id":"20fe53c853468187","repo":"siyuan-note/siyuan","slug":"skills-and-text-files-can-only-be-renamed-in-the-same","errorCode":null,"errorMessage":"skills and text files can only be renamed in the same directory","messagePattern":"skills and text files can only be renamed in the same directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/skill_manage.go","lineNumber":416,"sourceCode":"\t\t\treturn ret, errors.New(\"skill changed; reload it before saving, renaming or deleting\")\n\t\t}\n\t}\n\tswitch request.Action {\n\tcase \"write\":\n\t\tif info != nil && info.IsDir() {\n\t\t\treturn ret, errors.New(\"cannot write a skill directory\")\n\t\t}\n\t\tif err = writeManagedSkillSource(root, request.Path, request.Content, info == nil, request.Revision); err != nil {\n\t\t\treturn ret, err\n\t\t}\n\t\tret.Revision = fmt.Sprintf(\"%x\", sha256.Sum256([]byte(request.Content)))\n\t\treturn ret, nil\n\tcase \"move\":\n\t\tif err = checkManagedSkillPath(root, request.Target); err != nil {\n\t\t\treturn ret, err\n\t\t}\n\t\tif path.Dir(request.Path) != path.Dir(request.Target) {\n\t\t\treturn ret, errors.New(\"skills and text files can only be renamed in the same directory\")\n\t\t}\n\t\tif _, err = root.Lstat(request.Target); !errors.Is(err, os.ErrNotExist) {\n\t\t\treturn ret, errors.New(\"skill destination already exists or is inaccessible\")\n\t\t}\n\t\treturn ret, root.Rename(request.Path, request.Target)\n\tcase \"remove\":\n\t\tif info.IsDir() {\n\t\t\treturn ret, root.RemoveAll(request.Path)\n\t\t}\n\t\treturn ret, root.Remove(request.Path)\n\t}\n\treturn ret, errors.New(\"unsupported skill operation\")\n}\n","sourceCodeStart":398,"sourceCodeEnd":430,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/util/skill_manage.go#L398-L430","documentation":"The 'move' action only permits renames within a single directory of the skill root: the parent of request.Path must equal the parent of request.Target. This is enforced so managed skill files stay in their designated directory layout; cross-directory relocation is treated as an unsupported operation.","triggerScenarios":"Calling ManageSkillFiles with request.Action == \"move\" where path.Dir(request.Path) != path.Dir(request.Target), e.g. moving skills/foo/SKILL.md to skills/bar/SKILL.md.","commonSituations":"Developer tries to reorganize skills into subdirectories via move; a user-supplied target in a different folder; concatenating target paths with '..' components that escape the source directory.","solutions":["Keep Target in the same directory as Path (rename only)","Perform a cross-directory relocation manually: read the file, write it to the new location, then 'remove' the original","Adjust request.Target so its directory component matches request.Path"],"exampleFix":"// before\n{Action: \"move\", Path: \"skills/foo/SKILL.md\", Target: \"skills/bar/SKILL.md\"}\n// after (same-directory rename only)\n{Action: \"move\", Path: \"skills/foo/SKILL.md\", Target: \"skills/foo/SKILL.md.bak\"}","handlingStrategy":"validation","validationCode":"if path.Dir(srcPath) != path.Dir(targetPath) { /* do read+write+remove manually, or fix target */ }","typeGuard":null,"tryCatchPattern":"if err := ManageSkillFiles(req); err != nil { if strings.Contains(err.Error(), \"can only be renamed in the same directory\") { /* fall back to copy+remove */ } }","preventionTips":["Treat 'move' as rename-only; build relocation logic separately","Normalize both paths and compare directories before calling","Keep user-supplied targets within the same folder"],"tags":["skill-management","rename","path-constraint"],"backgroundTag":"unsupported-operation","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"}