vxcontrol/pentagi · error
%w: destination %q is a file; cannot move multiple sources i
Error message
%w: destination %q is a file; cannot move multiple sources into it
What it means
moveMultipleSources refuses to proceed when the destination directory path already exists but is a regular file, since multiple sources cannot be moved 'into' a file. It wraps errResourceConflict so callers can distinguish conflicts from invalid input. The transaction is rolled back before returning.
Source
Thrown at backend/pkg/server/services/resources.go:743
)
}
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 {
tx.Rollback()
return result, fmt.Errorf(
"%w: destination %q is a file; cannot move multiple sources into it",
errResourceConflict, dstPath,
)
}
if !destExists {
createdDirs, deletedDirs, orphanHashes, dirErr := ensureResourceDirs(tx, uid, dstPath, force)
if dirErr != nil {
tx.Rollback()
return result, dirErr
}
result.Added = append(result.Added, convertResources(createdDirs)...)
result.DeletedBefore = append(result.DeletedBefore, convertResources(deletedDirs)...)
result.OrphanHashes = append(result.OrphanHashes, orphanHashes...)
}
// ── Move each source into destination/<basename> ──────────────────────────
// The inner move functions (moveFileResource / moveDirResource) call
// ensureResourceDirs for the parent of the target path. Since dstPath isView on GitHub (pinned to ea665308ba)
Solutions
- Verify the destination is an existing directory (or does not exist) before calling MoveResource
- Use a different destination path or rename the conflicting file first
- Match errResourceConflict in the caller and surface 'destination is a file' to the user
Example fix
// before
svc.MoveResource(ctx, uid, sources, dstPath, false)
// after
dest, exists := findPath(dstPath)
if exists && !dest.IsDir {
return fmt.Errorf("destination %q is a file", dstPath)
}
svc.MoveResource(ctx, uid, sources, dstPath, false) Defensive patterns
Strategy: validation
Validate before calling
dest, exists := findResourceByPath(dstPath)
if exists && !dest.IsDir {
return fmt.Errorf("destination %q is a file", dstPath)
} Try / catch
err := svc.MoveResource(ctx, uid, sources, dstPath, false)
if errors.Is(err, services.ErrResourceConflict) {
// prompt user to pick another destination
} Prevention
- Resolve and inspect the destination before multi-source moves
- Pass directory paths, never file paths, as dstPath
- Refresh resource listings before drop-target operations
When it happens
Trigger: Calling MoveResource with a dstPath that resolves to an existing file resource instead of a directory, e.g. dstPath='/notes' where '/notes' is a stored file; a typo where the user intended '/notes/' as a directory.
Common situations: A file and directory share the same name after a prior rename; client passes a selected file's path instead of its parent folder as the drop target; stale UI cache shows a directory that is now a file.
Related errors
- %w: source and destination are the same for %q
- %w: cannot move directory into itself
- %w: resource %q already exists and is not a directory
- Token.InvalidRequest
- permission 'resources.view' required to use resource IDs
AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01).
Data as JSON: /api/errors/d1ff8aca79c6a0e3.
Report an issue: GitHub.