{"record":{"id":"0d7794673ce4f024","repo":"files-community/Files","slug":"failed-to-connect-to-ftp-server","errorCode":null,"errorMessage":"Failed to connect to FTP server.","messagePattern":"Failed to connect to FTP server\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs","lineNumber":195,"sourceCode":"\t\t\t\t{\n\t\t\t\t\tBaseStorageFile file = await destFolder.CreateFileAsync(desiredNewName, option.Convert());\n\t\t\t\t\tawait using var stream = await file.OpenStreamForWriteAsync();\n\t\t\t\t\treturn await ftpClient.DownloadStream(stream, FtpPath, token: cancellationToken) ? file : null;\n\t\t\t\t}\n\t\t\t}, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync));\n\t\t}\n\n\t\tpublic override IAsyncAction MoveAsync(IStorageFolder destinationFolder)\n\t\t\t=> MoveAsync(destinationFolder, Name, NameCollisionOption.FailIfExists);\n\t\tpublic override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string desiredNewName)\n\t\t\t=> MoveAsync(destinationFolder, desiredNewName, NameCollisionOption.FailIfExists);\n\t\tpublic override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string desiredNewName, NameCollisionOption option)\n\t\t{\n\t\t\treturn AsyncInfo.Run((cancellationToken) => SafetyExtensions.WrapAsync(async () =>\n\t\t\t{\n\t\t\t\tusing var ftpClient = GetFtpClient();\n\t\t\t\tif (!await ftpClient.EnsureConnectedAsync())\n\t\t\t\t\tthrow new IOException($\"Failed to connect to FTP server.\");\n\n\t\t\t\tBaseStorageFolder destFolder = destinationFolder.AsBaseStorageFolder();\n\n\t\t\t\tif (destFolder is FtpStorageFolder ftpFolder)\n\t\t\t\t{\n\t\t\t\t\tstring destName = $\"{ftpFolder.FtpPath}/{Name}\";\n\t\t\t\t\tFtpRemoteExists ftpRemoteExists = option is NameCollisionOption.ReplaceExisting ? FtpRemoteExists.Overwrite : FtpRemoteExists.Skip;\n\n\t\t\t\t\tbool isSuccessful = await ftpClient.MoveFile(FtpPath, destName, ftpRemoteExists, cancellationToken);\n\t\t\t\t\tif (!isSuccessful)\n\t\t\t\t\t\tthrow new IOException($\"Failed to move file from {Path} to {destFolder}.\");\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t\tthrow new NotSupportedException();\n\t\t\t}, ((IPasswordProtectedItem)this).RetryWithCredentialsAsync));\n\t\t}\n\n","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/files-community/Files/blob/68c68a58d4d6a5f7197e07c8fff85cfd4279c78b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFile.cs#L177-L213","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm connectivity before issuing the move.\nusing var c = GetFtpClient();\nif (!await c.EnsureConnectedAsync())\n    throw new IOException(\"Cannot reach FTP server; check credentials and network.\");","typeGuard":null,"tryCatchPattern":"try { await file.MoveAsync(dest, name, option); }\ncatch (IOException ex) when (ex.Message.Contains(\"connect to FTP\"))\n{ /* re-prompt credentials via IPasswordProtectedItem, then retry */ }","preventionTips":["Re-prompt credentials through the RetryWithCredentialsAsync hook.","Verify host/port/encryption settings.","Confirm firewall allows control and passive data ports."],"tags":["ftp","storage","move","network","connection"],"backgroundTag":null,"analyzedSha":"68c68a58d4d6a5f7197e07c8fff85cfd4279c78b","analyzedAt":"2026-08-13T10:34:54.614Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}