{"record":{"id":"55d9ea376ac5d77e","repo":"AlistGo/alist","slug":"cannot-move-parent-dir-to-child-55d9ea","errorCode":null,"errorMessage":"cannot move parent dir to child","messagePattern":"cannot move parent dir to child","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/url_tree/driver.go","lineNumber":161,"sourceCode":"\t}\n\tif node.isFile() {\n\t\treturn nil, errs.NotFolder\n\t}\n\tdir := &Node{\n\t\tName:  dirName,\n\t\tLevel: node.Level + 1,\n\t}\n\tnode.Children = append(node.Children, dir)\n\td.updateStorage()\n\treturn nodeToObj(dir, stdpath.Join(parentDir.GetPath(), dirName))\n}\n\nfunc (d *Urls) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {\n\tif !d.Writable {\n\t\treturn nil, errs.PermissionDenied\n\t}\n\tif strings.HasPrefix(dstDir.GetPath(), srcObj.GetPath()) {\n\t\treturn nil, errors.New(\"cannot move parent dir to child\")\n\t}\n\td.mutex.Lock()\n\tdefer d.mutex.Unlock()\n\tdstNode := GetNodeFromRootByPath(d.root, dstDir.GetPath())\n\tif dstNode == nil || dstNode.isFile() {\n\t\treturn nil, errs.NotFolder\n\t}\n\tsrcDir, srcName := stdpath.Split(srcObj.GetPath())\n\tsrcParentNode := GetNodeFromRootByPath(d.root, srcDir)\n\tif srcParentNode == nil {\n\t\treturn nil, errs.ObjectNotFound\n\t}\n\tnewChildren := make([]*Node, 0, len(srcParentNode.Children))\n\tvar srcNode *Node\n\tfor _, child := range srcParentNode.Children {\n\t\tif child.Name == srcName {\n\t\t\tsrcNode = child\n\t\t} else {","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/url_tree/driver.go#L143-L179","documentation":"The url_tree driver's Move() rejects moves where the destination directory's path is prefixed by the source object's path — i.e. trying to move a folder into one of its own descendants. That would create a cycle in the tree, so the driver blocks it before touching storage.","triggerScenarios":"Move(src=/a, dst=/a/b/c): strings.HasPrefix(\"/a/b/c\", \"/a\") is true, so any move of a folder into a subfolder of itself fails immediately. Note the check uses raw path-prefix HasPrefix, so '/ab' also prefixes '/abc-b...' style collisions are guarded only by exact path semantics — e.g. dst '/afile' would falsely match src '/a' because HasPrefix does not respect path segments.","commonSituations":"UI drag-and-drop of a parent folder into its own subfolder; Programmatic renames that accidentally set the destination inside the source subtree; Edge case: sibling directories whose names share a prefix (src '/data', dst '/data-backup') can be wrongly rejected due to the segment-unaware HasPrefix check","solutions":["Choose a destination outside the source subtree (e.g. move /a to /b, not /a/b)","If the rejection looks wrong, check for shared name prefixes — dst '/data2' under src '/data' is a false positive; work around by renaming the destination first","Driver fix: compare path segments (dstDir.GetPath() == srcObj.GetPath() || strings.HasPrefix(dstDir.GetPath(), srcObj.GetPath()+\"/\")) instead of raw HasPrefix"],"exampleFix":"// before\nif strings.HasPrefix(dstDir.GetPath(), srcObj.GetPath()) {\n\treturn nil, errors.New(\"cannot move parent dir to child\")\n}\n\n// after (segment-aware)\nsrc := strings.TrimSuffix(srcObj.GetPath(), \"/\")\nif dstDir.GetPath() == src || strings.HasPrefix(dstDir.GetPath(), src+\"/\") {\n\treturn nil, errors.New(\"cannot move parent dir to child\")\n}","handlingStrategy":"validation","validationCode":"src := strings.TrimSuffix(srcObj.GetPath(), \"/\")\nif dstDir.GetPath() == src || strings.HasPrefix(dstDir.GetPath(), src+\"/\") {\n    return fmt.Errorf(\"cannot move %s into its own subtree %s\", src, dstDir.GetPath())\n}","typeGuard":"func isMoveIntoSelf(src, dst string) bool {\n    src = strings.TrimSuffix(src, \"/\")\n    return dst == src || strings.HasPrefix(dst, src+\"/\")\n}","tryCatchPattern":"if _, err := d.Move(ctx, srcObj, dstDir); err != nil {\n    if strings.Contains(err.Error(), \"cannot move parent dir to child\") {\n        // pick a destination outside the source subtree, or restructure first\n    }\n}","preventionTips":["Validate destination paths against the source subtree before issuing Move","Use segment-aware prefix checks (append '/') to avoid false rejections on sibling names","Restrict UI copy/move targets to directories outside the dragged subtree"],"tags":["url-tree","move","filesystem","cycle-prevention"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}