yorukot/superfile · error
failed to remove source after move: %w
Error message
failed to remove source after move: %w
What it means
pasteDir wraps os.RemoveAll(src) failures when finishing a cross-device cut (move) that fell back to a manual copy. The move copied successfully but cleanup of the original failed, so the source remains and the wrapped cause (usually EPERM/EBUSY/ENOTEMPTY) is reported.
Source
Thrown at src/internal/file_operations.go:195
}
relPath, err := filepath.Rel(src, path)
if err != nil {
return err
}
newPath := filepath.Join(dst, relPath)
return actualPasteOperation(info, path, newPath, cut, sameDev, p, processBarModel)
})
if err != nil {
return err
}
// If this was a cut operation and we had to do a manual copy, remove the source
if cut && !sameDev {
err = os.RemoveAll(src)
if err != nil {
return fmt.Errorf("failed to remove source after move: %w", err)
}
}
return nil
}
func actualPasteOperation(info os.FileInfo, path string, newPath string, cut bool, sameDev bool,
p *processbar.Process, processBarModel *processbar.Model) error {
var err error
if info.IsDir() {
// TODO - this is likely not needed because we did
// dst, err := renameIfDuplicate(dst) above
newPath, err = renameIfDuplicate(newPath)
if err != nil {
return err
}
err = os.MkdirAll(newPath, info.Mode())
return errView on GitHub (pinned to b72f550bc6)
Solutions
- Close applications holding files in the source directory open (editors, indexers, terminals cd'd into it), then retry the move.
- Check write/execute permissions on the source directory and its contents (chmod -R u+w).
- Remove the leftover source manually once unlocked: rm -rf <src> (verify the copy first).
- Same-device moves avoid this path entirely: paste onto the same filesystem so rename() is used instead of copy+delete.
Example fix
// before
if cut && !sameDev {
err = os.RemoveAll(src)
if err != nil {
return fmt.Errorf("failed to remove source after move: %w", err)
}
}
// after
if cut && !sameDev {
if err := os.RemoveAll(src); err != nil {
return fmt.Errorf("move succeeded but could not delete source %s (is a file open elsewhere?): %w", src, err)
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// cross-device move will copy+delete; make sure no process holds the tree
// (best effort) verify write access to source root
if f, err := os.OpenFile(src, os.O_RDONLY, 0); err != nil {
return fmt.Errorf("source may not be removable: %w", err)
} else {
f.Close()
} Try / catch
if err := ops.Paste(src, dst, cut=true); err != nil {
if strings.Contains(err.Error(), "failed to remove source after move") {
// copy succeeded; ask user to close apps holding source files, then rm -rf manually
}
return err
} Prevention
- Move within the same filesystem when possible so rename() is used.
- Close editors/indexers before cross-device moves.
- Verify the copied destination before deleting the source manually.
When it happens
Trigger: A cut-paste across filesystems (rename fails EXDEV, so pasteDir does copy+delete) where the source cannot be removed: a file inside is held open (EBUSY on Windows), permission denied, or a new file appeared in the source dir making it non-empty for a non-recursive failure.
Common situations: Moving directories between drives/partitions (e.g. home -> USB) while an editor or indexer holds files open; moving dirs with files owned by other users; Windows AV scanners locking files.
Related errors
- failed to get absolute path of the first path: %w
- failed to get absolute path of the second path: %w
- failed to check partitions: %w
- failed to copy: %w
- failed to remove source after copy: %w
AI-assisted analysis of yorukot/superfile@b72f550bc6 (2026-09-01).
Data as JSON: /api/errors/093b0666c3225f29.
Report an issue: GitHub.