vxcontrol/pentagi · warning

%w: cannot move directory into itself

Error message

%w: cannot move directory into itself

What it means

moveDirResource rejects a move where the destination path lies inside the source directory (PathHasPrefix), which would create a cycle and corrupt the tree. It wraps errResourceInvalid so callers can classify it as caller error rather than a server failure. No changes are made.

Source

Thrown at backend/pkg/server/services/resources.go:935

	}

	updated, err := updateMovedResource(tx, src, targetPath, time.Now())
	if err != nil {
		return result, err
	}
	result.Updated = append(result.Updated, convertResource(updated))
	return result, nil
}

func (s *ResourceService) moveDirResource(
	tx *gorm.DB,
	uid uint64,
	srcPath, dstPath string,
	force bool,
) (moveResourceResult, error) {
	result := moveResourceResult{}
	if resources.PathHasPrefix(dstPath, srcPath) {
		return result, fmt.Errorf("%w: cannot move directory into itself", errResourceInvalid)
	}
	// Moving a directory to root (dstPath=="") means placing it at root level
	if dstPath == "" {
		dstPath = path.Base(srcPath)
	}

	createdParents, deletedParents, orphanHashes, err := ensureResourceDirs(tx, uid, resources.ParentDir(dstPath), force)
	if err != nil {
		return result, err
	}
	result.Added = append(result.Added, convertResources(createdParents)...)
	result.DeletedBefore = append(result.DeletedBefore, convertResources(deletedParents)...)
	result.OrphanHashes = append(result.OrphanHashes, orphanHashes...)

	dest, destExists, err := findResourceByPath(tx, uid, dstPath)
	if err != nil {
		return result, err
	}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Validate that dstPath does not start with srcPath + "/" before calling
  2. Match errors.Is(err, errResourceInvalid) and reject the operation in the UI
  3. Normalize both paths (clean trailing slashes) before comparison to avoid false negatives

Example fix

// before
svc.MoveResource(ctx, uid, []Source{{Path: "/a"}}, "/a/sub", false)
// after
if resources.PathHasPrefix("/a/sub", "/a") {
    return fmt.Errorf("cannot move %q into itself", "/a")
}
svc.MoveResource(ctx, uid, []Source{{Path: "/a"}}, "/a/sub", false)
Defensive patterns

Strategy: validation

Validate before calling

if resources.PathHasPrefix(dstPath, srcPath) {
    return fmt.Errorf("refusing move: %q into %q", srcPath, dstPath)
}

Try / catch

err := svc.MoveResource(ctx, uid, []Source{{Path: srcPath}}, dstPath, false)
if errors.Is(err, services.ErrResourceInvalid) {
    // reject in UI: cannot move folder into itself
}

Prevention

When it happens

Trigger: Calling MoveResource with dstPath='/a/b' and srcPath='/a' (or dstPath equal to srcPath), e.g. moving directory /a into /a/sub.

Common situations: Recursive filesystem walks that try to move a parent into its own child; UI allowing a folder to be dropped into one of its descendants; scripts building destination paths by string concatenation without prefix checks.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/a41285dd5129931a. Report an issue: GitHub.