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

  1. Choose a destination outside the source subtree (e.g. move /a to /b, not /a/b)
  2. 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
  3. 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

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


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/55d9ea376ac5d77e. Report an issue: GitHub.