files-community/Files · error · IOException
Failed to move folder from {Path} to {destFolder}.
Error message
Failed to move folder from {Path} to {destFolder}. What it means
Thrown by FtpStorageFolder.MoveAsync (Files.App/Utils) when AsyncFtpClient.MoveDirectory returns false after a successful connection. The directory rename did not complete: destination collision under skip mode, source missing, permission denied, or the server rejecting the move (e.g. cross-root renames).
Source
Thrown at src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs:284
=> MoveAsync(destinationFolder, NameCollisionOption.FailIfExists);
public override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option)
{
return AsyncInfo.Run((cancellationToken) => SafetyExtensions.Wrap<BaseStorageFolder>(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.MoveDirectory(FtpPath, destName, ftpRemoteExists, token: cancellationToken);
if (!isSuccessful)
throw new IOException($"Failed to move folder from {Path} to {destFolder}.");
var folder = new FtpStorageFolder(new StorageFileWithPath(null, destName));
((IPasswordProtectedItem)folder).CopyFrom(this);
return folder;
}
else
throw new NotSupportedException();
}, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync));
}
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
- Use NameCollisionOption.ReplaceExisting if overwriting the destination is acceptable.
- Pre-check destination existence with DirectoryExists and resolve collisions before moving.
- Inspect ftpClient.LastReply for the 5xx code explaining the failure.
- Confirm the source FtpPath still exists immediately before the move.
- Catch IOException, distinguish collision vs permission, and retry with the right strategy.
Example fix
// before
await folder.MoveAsync(destFolder, NameCollisionOption.FailIfExists);
// after
var option = await DestFolderExists(destFolder, folder.Name)
? NameCollisionOption.ReplaceExisting
: NameCollisionOption.FailIfExists;
await folder.MoveAsync(destFolder, option); Defensive patterns
Strategy: try-catch
Validate before calling
// Resolve destination collisions before moving a folder.
bool exists = await ftpClient.DirectoryExists($"{ftpFolder.FtpPath}/{folder.Name}", ct);
var option = exists ? NameCollisionOption.ReplaceExisting : NameCollisionOption.FailIfExists; Try / catch
try { await folder.MoveAsync(destFolder, option); }
catch (IOException ex) when (ex.Message.Contains("Failed to move folder"))
{ 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: MoveDirectory returning false inside MoveAsync. With NameCollisionOption other than ReplaceExisting, FtpRemoteExists.Skip is used so an existing destination directory causes failure. Insufficient permission, missing source, or unsupported cross-filesystem rename also yield false.
Common situations: Moving a folder onto an existing name without ReplaceExisting; source folder deleted between list and move; lack of write permission at destination; servers that forbid renaming non-empty directories.
Related errors
- Failed to move file 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/556b30700c33c256.
Report an issue: GitHub.