yorukot/superfile · error

failed to remove source after copy: %w

Error message

failed to remove source after copy: %w

What it means

After copyElement succeeds, moveElement removes the original with os.RemoveAll(src); this error wraps a failure of that removal. At this point the data is already copied — the destination is safe — but the source could not be deleted, so the move is incomplete and a duplicate remains.

Source

Thrown at src/internal/file_operations.go:71

	}

	// If on the same partition, attempt to rename (which will use the same inode)
	if sameDev {
		if err = os.Rename(src, dst); err == nil {
			return nil
		}
		// If rename fails, fall back to copy+delete
	}

	// If on different partitions or rename failed, fall back to copy+delete
	err = copyElement(src, dst)
	if err != nil {
		return fmt.Errorf("failed to copy: %w", err)
	}

	err = os.RemoveAll(src)
	if err != nil {
		return fmt.Errorf("failed to remove source after copy: %w", err)
	}

	return nil
}

// copyElement handles copying of both files and directories
func copyElement(src, dst string) error {
	srcInfo, err := os.Stat(src)
	if err != nil {
		return fmt.Errorf("failed to stat source: %w", err)
	}

	if srcInfo.IsDir() {
		return copyDir(src, dst, srcInfo)
	}
	return copyFile(src, dst, srcInfo)
}

View on GitHub (pinned to b72f550bc6)

Solutions

  1. Free/fix permissions on the source tree (chmod u+w, correct ownership) and delete it manually.
  2. Close processes holding files in the source (on Windows, check locks).
  3. Since the copy already succeeded, manually remove the leftover source with rm -rf after verifying the destination.
  4. Retry the move after resolving the lock; or treat it as a copy and clean up yourself.

Example fix

// before
chmod 555 /data/src           // removal fails after copy
// after
chmod -R u+w /data/src && rm -rf /data/src
Defensive patterns

Strategy: fallback

Validate before calling

// ensure every entry in src is deletable before the move
err := filepath.WalkDir(src, func(p string, d fs.DirEntry, err error) error {
    if err != nil { return err }
    if !d.IsDir() {
        if info, _ := d.Info(); info != nil && info.Mode().Perm()&0200 == 0 {
            return fmt.Errorf("read-only file blocks removal: %s", p)
        }
    }
    return nil
})

Type guard

func sourceStillExists(src string) bool {
    _, err := os.Stat(src)
    return err == nil
}

Try / catch

if err := moveElement(src, dst); err != nil {
    if strings.Contains(err.Error(), "failed to remove source after copy") {
        log.Warnf("Copy succeeded; leftover source at %s — clean up manually", src)
        // data is safe at dst
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: moveElement's copy phase succeeded but os.RemoveAll(src) fails: a file inside the source tree became read-only/owned by another user, a file was locked (Windows), or a new file appeared with no delete permission.

Common situations: Windows antivirus or another process holding the source open; source directory containing files owned by root; source files with permission 444/555; source modified concurrently during the copy.

Related errors


AI-assisted analysis of yorukot/superfile@b72f550bc6 (2026-09-01). Data as JSON: /api/errors/4a9e00cee8f000c0. Report an issue: GitHub.