{"record":{"id":"556b30700c33c256","repo":"files-community/Files","slug":"failed-to-move-folder-from-path-to-destfolder","errorCode":null,"errorMessage":"Failed to move folder from {Path} to {destFolder}.","messagePattern":"Failed to move folder from (.+?) to (.+?)\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs","lineNumber":284,"sourceCode":"\t\t\t=> MoveAsync(destinationFolder, NameCollisionOption.FailIfExists);\n\t\tpublic override IAsyncOperation<BaseStorageFolder> MoveAsync(IStorageFolder destinationFolder, NameCollisionOption option)\n\t\t{\n\t\t\treturn AsyncInfo.Run((cancellationToken) => SafetyExtensions.Wrap<BaseStorageFolder>(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.MoveDirectory(FtpPath, destName, ftpRemoteExists, token: cancellationToken);\n\t\t\t\t\tif (!isSuccessful)\n\t\t\t\t\t\tthrow new IOException($\"Failed to move folder from {Path} to {destFolder}.\");\n\n\t\t\t\t\tvar folder = new FtpStorageFolder(new StorageFileWithPath(null, destName));\n\t\t\t\t\t((IPasswordProtectedItem)folder).CopyFrom(this);\n\t\t\t\t\treturn folder;\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\t\tpublic override IAsyncAction RenameAsync(string desiredName)\n\t\t\t=> RenameAsync(desiredName, NameCollisionOption.FailIfExists);\n\t\tpublic override IAsyncAction RenameAsync(string desiredName, 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())","sourceCodeStart":266,"sourceCodeEnd":302,"githubUrl":"https://github.com/files-community/Files/blob/68c68a58d4d6a5f7197e07c8fff85cfd4279c78b/src/Files.App/Utils/Storage/StorageItems/FtpStorageFolder.cs#L266-L302","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait folder.MoveAsync(destFolder, NameCollisionOption.FailIfExists);\n\n// after\nvar option = await DestFolderExists(destFolder, folder.Name)\n    ? NameCollisionOption.ReplaceExisting\n    : NameCollisionOption.FailIfExists;\nawait folder.MoveAsync(destFolder, option);","handlingStrategy":"try-catch","validationCode":"// Resolve destination collisions before moving a folder.\nbool exists = await ftpClient.DirectoryExists($\"{ftpFolder.FtpPath}/{folder.Name}\", ct);\nvar option = exists ? NameCollisionOption.ReplaceExisting : NameCollisionOption.FailIfExists;","typeGuard":null,"tryCatchPattern":"try { await folder.MoveAsync(destFolder, option); }\ncatch (IOException ex) when (ex.Message.Contains(\"Failed to move folder\"))\n{ var reply = ftpClient.LastReply; /* inspect 5xx */ throw; }","preventionTips":["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."],"tags":["ftp","storage","move","collision"],"backgroundTag":null,"analyzedSha":"68c68a58d4d6a5f7197e07c8fff85cfd4279c78b","analyzedAt":"2026-08-13T10:34:54.614Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}