files-community/Files · error · IOException
Failed to move file from {Path} to {destFolder}.
Error message
Failed to move file from {Path} to {destFolder}. What it means
Thrown by FtpStorageFile.MoveAsync when AsyncFtpClient.MoveFile returns false after a successful connection. The RNFR/RNTO rename did not complete: the destination already exists with skip mode, the source path no longer exists, the user lacks permission, or the server rejected the rename.
Source
Thrown at src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs:206
=> MoveAsync(destinationFolder, desiredNewName, NameCollisionOption.FailIfExists);
public override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string desiredNewName, NameCollisionOption option)
{
return AsyncInfo.Run((cancellationToken) => SafetyExtensions.WrapAsync(async () =>
{
using var ftpClient = GetFtpClient();
if (!await ftpClient.EnsureConnectedAsync())
throw new IOException($"Failed to connect to FTP server.");
BaseStorageFolder destFolder = destinationFolder.AsBaseStorageFolder();
if (destFolder is FtpStorageFolder ftpFolder)
{
string destName = $"{ftpFolder.FtpPath}/{Name}";
FtpRemoteExists ftpRemoteExists = option is NameCollisionOption.ReplaceExisting ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;
bool isSuccessful = await ftpClient.MoveFile(FtpPath, destName, ftpRemoteExists, cancellationToken);
if (!isSuccessful)
throw new IOException($"Failed to move file from {Path} to {destFolder}.");
}
else
throw new NotSupportedException();
}, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync));
}
public override IAsyncAction CopyAndReplaceAsync(IStorageFile fileToReplace) => throw new NotSupportedException();
public override IAsyncAction MoveAndReplaceAsync(IStorageFile fileToReplace) => throw new NotSupportedException();
public override IAsyncAction RenameAsync(string desiredName)
=> RenameAsync(desiredName, NameCollisionOption.FailIfExists);
public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption option)
{
return AsyncInfo.Run((cancellationToken) => SafetyExtensions.WrapAsync(async () =>
{
using var ftpClient = GetFtpClient();
if (!await ftpClient.EnsureConnectedAsync())View on GitHub (pinned to 68c68a58d4)
Solutions
- If overwriting is intended, call MoveAsync with NameCollisionOption.ReplaceExisting so FtpRemoteExists.Overwrite is used.
- Pre-check the destination with DirectoryExists/FileExists and resolve collisions (rename, skip, or overwrite) at the call site.
- Inspect the FluentFTP reply (LastReply) for the 5xx code explaining why MoveFile failed.
- Verify the source FtpPath still exists immediately before the move.
- Catch IOException, confirm whether a destination collision is the cause, and retry accordingly.
Example fix
// before
await file.MoveAsync(destFolder, file.Name, NameCollisionOption.FailIfExists);
// after
var option = await DestExists(destFolder, file.Name)
? NameCollisionOption.ReplaceExisting
: NameCollisionOption.FailIfExists;
await file.MoveAsync(destFolder, file.Name, option); Defensive patterns
Strategy: try-catch
Validate before calling
// Resolve destination collisions before moving.
bool exists = await ftpClient.FileExists($"{ftpFolder.FtpPath}/{file.Name}", ct);
var option = exists ? NameCollisionOption.ReplaceExisting : NameCollisionOption.FailIfExists; Try / catch
try { await file.MoveAsync(destFolder, file.Name, option); }
catch (IOException ex) when (ex.Message.Contains("Failed to move file"))
{ var reply = ftpClient.LastReply; /* inspect 5xx */ throw; } Prevention
- Pre-check destination existence and choose ReplaceExisting when overwriting.
- Confirm the source path still exists immediately before moving.
- Inspect LastReply to distinguish collision from permission denial.
When it happens
Trigger: MoveFile returning false inside MoveAsync. With NameCollisionOption other than ReplaceExisting, FtpRemoteExists.Skip is used, so an existing destination file causes the move to be skipped/rejected. Permission denied, source-not-found, or cross-device rename restrictions also produce false.
Common situations: Moving a file onto a name that already exists without choosing ReplaceExisting; the source file was deleted between listing and move; insufficient write permission at the destination; renaming across different FTP roots the server does not allow.
Related errors
- Failed to move folder from {Path} to {destFolder}.
- Couldn't generate unique name. File skipped.
- Failed to connect to FTP server.
- Copying folders is not supported.
- File already exists.
AI-assisted analysis of files-community/Files@68c68a58d4 (2026-08-13).
Data as JSON: /api/errors/ba1d1286924c267d.
Report an issue: GitHub.