vxcontrol/pentagi · error
%w: cannot copy directory %q into itself
Error message
%w: cannot copy directory %q into itself
What it means
copyMultipleSources rejects a batch where a source directory is an ancestor of (or equal to) the destination path — the copy would recursively contain itself, producing infinite nesting. The guard uses resources.PathHasPrefix on the destination, returns errResourceInvalid, and rolls back the transaction.
Source
Thrown at backend/pkg/server/services/resources.go:1361
// ── Within-batch target-basename conflict check ───────────────────────────
basenameToSrc := make(map[string]string, len(srcs))
for _, src := range srcs {
if prev, conflict := basenameToSrc[src.Name]; conflict {
tx.Rollback()
return result, fmt.Errorf(
"%w: sources %q and %q share the same base name %q",
errResourceConflict, prev, src.Path, src.Name,
)
}
basenameToSrc[src.Name] = src.Path
}
// Self-copy guard: copying a directory into itself.
for _, src := range srcs {
if src.IsDir && resources.PathHasPrefix(dstPath, src.Path) {
tx.Rollback()
return result, fmt.Errorf(
"%w: cannot copy directory %q into itself", errResourceInvalid, src.Path,
)
}
if resources.FilePath(dstPath, src.Name) == src.Path {
tx.Rollback()
return result, fmt.Errorf(
"%w: source and destination are the same for %q", errResourceInvalid, src.Path,
)
}
}
// ── Ensure destination directory ──────────────────────────────────────────
dest, destExists, err := findResourceByPath(tx, uid, dstPath)
if err != nil {
tx.Rollback()
return result, err
}
if destExists && !dest.IsDir {View on GitHub (pinned to ea665308ba)
Solutions
- Choose a destination outside every source directory in the batch.
- If a copy into the folder is truly needed, copy the folder's contents to a sibling directory instead.
- Validate client-side: reject when dstPath === src.path or dstPath starts with src.path + '/'.
- For single-directory duplication use single-source mode, which handles the self-copy case differently.
Example fix
// before
copy({ sources: ["docs"], destination: "docs/archive" }) // invalid: docs into itself
// after
copy({ sources: ["docs"], destination: "backup/docs" }) Defensive patterns
Strategy: validation
Validate before calling
for _, s := range dirSources {
if dst == s || strings.HasPrefix(dst, s+"/") {
return fmt.Errorf("destination %q is inside source %q", dst, s)
}
} Type guard
func dstInsideSource(dst, src string) bool {
return dst == src || strings.HasPrefix(dst, strings.TrimSuffix(src, "/")+"/")
} Try / catch
err := copyMulti(ctx, sources, dst)
if err != nil && errors.Is(err, errResourceInvalid) {
// choose a destination outside all source directories and retry
} Prevention
- Disable the current folder as a copy destination in folder pickers.
- Validate dst not under any source before submit.
- When duplicating a tree, copy to a sibling path.
- Write a shared helper for the ancestor check used everywhere.
When it happens
Trigger: Multi-source copy with destination inside or equal to one of the source directories, e.g. sources ['docs'] and destination 'docs/archive', or destination 'docs' itself.
Common situations: UI letting users pick the currently-open folder as the copy target; scripting a copy of a project into its own subfolder; off-by-one path joins that place dstPath under a source.
Related errors
- %w: cannot move directory into itself
- unsupported subtask status: %s
- reading uploads cache: %w
- reading container cache: %w
- reading resources cache: %w
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/5f7bd5d8eb2cacfd.
Report an issue: GitHub.