Skip to content
Auto
Products

Conflicts and errors

Copy, Move, CopyTo and MoveTo transfer whole trees, so two questions have to be answered before they run: what happens when the destination already exists, and what happens when one item in the middle fails. Each method takes an overload for both.

The shortest form takes a flag. false is the default, so a call that omits it fails on an existing destination:

sourceFile.Copy(destinationFile, overwrite: true);

FileSystemConflictResolveMode gives the remaining answers:

ModeResult
SkipThe item is left as it is and the transfer continues.
OverwriteThe destination is replaced.
MergeDirectoriesThe two directories are merged instead of one replacing the other.
ChangeSourceNameThe transferred item takes the next free name at the destination, so the existing one survives.
ErrorThe operation stops and throws.
sourceDirectory.Copy(destinationDirectory, FileSystemConflictResolveMode.MergeDirectories);

A whole tree rarely wants one answer for every item. FileSystemConflictResolveDelegate is asked per conflict and receives both sides:

sourceDirectory.Copy(destinationDirectory, (source, destination) =>
source is FileLink file && file.ModifyTime > ((FileLink)destination).ModifyTime
? FileSystemConflictResolveMode.Overwrite
: FileSystemConflictResolveMode.Skip);

Without an exception resolver, the first failure ends the transfer and the exception reaches the caller. Half of a directory may already have been copied.

FileSystemExceptionResolveDelegate is the last parameter of every Copy and Move overload. It receives the failing pair, the exception and the attempt number, and decides what happens next:

ModeResult
SkipThe failed item is left unprocessed and the transfer continues with the next one.
RetryThe failed operation is attempted again.
ErrorThe operation stops and rethrows to the caller.
sourceDirectory.Copy(destinationDirectory, overwrite: true, (source, destination, exception, attempt) =>
exception is IOException && attempt < 3
? FileSystemExceptionResolveMode.Retry
: FileSystemExceptionResolveMode.Skip);

The attempt counter is what keeps Retry from looping. A resolver that returns Retry unconditionally never gives up.

CopyAsync, MoveAsync, CopyToAsync and MoveToAsync take the same parameters in the same positions. The resolvers stay synchronous, because they only choose a mode.

await sourceDirectory.CopyAsync(destinationDirectory, FileSystemConflictResolveMode.Skip);