files-community/Files · error · IOException
Failed to connect to FTP server.
Error message
Failed to connect to FTP server.
What it means
Thrown by FtpStorageFile.MoveAsync when EnsureConnectedAsync returns false - the FTP client could not establish or restore a connection (credentials rejected, host unreachable, TLS handshake failure). The operation runs inside SafetyExtensions.WrapAsync with a RetryWithCredentialsAsync hook, so a credentials retry is attempted before this surfaces.
Source
Thrown at src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs:195
{
BaseStorageFile file = await destFolder.CreateFileAsync(desiredNewName, option.Convert());
await using var stream = await file.OpenStreamForWriteAsync();
return await ftpClient.DownloadStream(stream, FtpPath, token: cancellationToken) ? file : null;
}
}, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync));
}
public override IAsyncAction MoveAsync(IStorageFolder destinationFolder)
=> MoveAsync(destinationFolder, Name, NameCollisionOption.FailIfExists);
public override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string desiredNewName)
=> 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));
}
View on GitHub (pinned to 68c68a58d4)
Solutions
- Prompt the user to re-enter credentials (the RetryWithCredentialsAsync path) and retry the move.
- Verify host, port, and encryption settings match the server (explicit vs implicit TLS, correct port).
- Check network connectivity and firewall rules for both the control channel and passive data ports.
- Wrap MoveAsync in a try/catch for IOException and report a connection-level message rather than a move-level one.
- Recreate the FtpStorageFile from a fresh FtpStorageFolder if the underlying client is stale.
Defensive patterns
Strategy: retry
Validate before calling
// Confirm connectivity before issuing the move.
using var c = GetFtpClient();
if (!await c.EnsureConnectedAsync())
throw new IOException("Cannot reach FTP server; check credentials and network."); Try / catch
try { await file.MoveAsync(dest, name, option); }
catch (IOException ex) when (ex.Message.Contains("connect to FTP"))
{ /* re-prompt credentials via IPasswordProtectedItem, then retry */ } Prevention
- Re-prompt credentials through the RetryWithCredentialsAsync hook.
- Verify host/port/encryption settings.
- Confirm firewall allows control and passive data ports.
When it happens
Trigger: MoveAsync on an FtpStorageFile when the underlying AsyncFtpClient fails to connect before issuing the RNFR/RNTO sequence. Typically credentials are wrong/expired, the host is unreachable, the port is blocked, or the TLS negotiation failed.
Common situations: Saved FTP bookmark with an expired password; server moved or is temporarily down; corporate firewall blocking the control port (21) or PASV data ports; certificate changes breaking TLS; connection pool handing back a dead client.
Related errors
- Failed to connect to FTP server.
- File creation failed.
- Directory was not successfully created.
- Failed to move file from {Path} to {destFolder}.
- Failed to create file {remotePath}.
AI-assisted analysis of files-community/Files@68c68a58d4 (2026-08-13).
Data as JSON: /api/errors/0d7794673ce4f024.
Report an issue: GitHub.