AlistGo/alist · error
cannot move parent dir to child
Error message
cannot move parent dir to child
What it means
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.
Source
Thrown at drivers/url_tree/driver.go:161
}
if node.isFile() {
return nil, errs.NotFolder
}
dir := &Node{
Name: dirName,
Level: node.Level + 1,
}
node.Children = append(node.Children, dir)
d.updateStorage()
return nodeToObj(dir, stdpath.Join(parentDir.GetPath(), dirName))
}
func (d *Urls) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
if !d.Writable {
return nil, errs.PermissionDenied
}
if strings.HasPrefix(dstDir.GetPath(), srcObj.GetPath()) {
return nil, errors.New("cannot move parent dir to child")
}
d.mutex.Lock()
defer d.mutex.Unlock()
dstNode := GetNodeFromRootByPath(d.root, dstDir.GetPath())
if dstNode == nil || dstNode.isFile() {
return nil, errs.NotFolder
}
srcDir, srcName := stdpath.Split(srcObj.GetPath())
srcParentNode := GetNodeFromRootByPath(d.root, srcDir)
if srcParentNode == nil {
return nil, errs.ObjectNotFound
}
newChildren := make([]*Node, 0, len(srcParentNode.Children))
var srcNode *Node
for _, child := range srcParentNode.Children {
if child.Name == srcName {
srcNode = child
} else {View on GitHub (pinned to 843d9dc814)
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
Example fix
// before
if strings.HasPrefix(dstDir.GetPath(), srcObj.GetPath()) {
return nil, errors.New("cannot move parent dir to child")
}
// after (segment-aware)
src := strings.TrimSuffix(srcObj.GetPath(), "/")
if dstDir.GetPath() == src || strings.HasPrefix(dstDir.GetPath(), src+"/") {
return nil, errors.New("cannot move parent dir to child")
} Defensive patterns
Strategy: validation
Validate before calling
src := strings.TrimSuffix(srcObj.GetPath(), "/")
if dstDir.GetPath() == src || strings.HasPrefix(dstDir.GetPath(), src+"/") {
return fmt.Errorf("cannot move %s into its own subtree %s", src, dstDir.GetPath())
} Type guard
func isMoveIntoSelf(src, dst string) bool {
src = strings.TrimSuffix(src, "/")
return dst == src || strings.HasPrefix(dst, src+"/")
} Try / catch
if _, err := d.Move(ctx, srcObj, dstDir); err != nil {
if strings.Contains(err.Error(), "cannot move parent dir to child") {
// pick a destination outside the source subtree, or restructure first
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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
Related errors
- cannot copy parent dir to child
- can't move files between two storages, try to copy
- merge: list source folder failed: %w
- same-name files cannot be moved
- same-name dirs cannot be moved to
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/55d9ea376ac5d77e.
Report an issue: GitHub.