{"record":{"id":"6c2e5e24a2f515de","repo":"files-community/Files","slug":"copying-folders-is-not-supported","errorCode":null,"errorMessage":"Copying folders is not supported.","messagePattern":"Copying folders is not supported\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"warning","filePath":"src/Files.App.Storage/Ftp/FtpStorageFolder.cs","lineNumber":110,"sourceCode":"\t\t\telse\n\t\t\t{\n\t\t\t\tthrow new ArgumentException($\"Could not delete {item}.\");\n\t\t\t}\n\t\t}\n\n\t\t/// <inheritdoc/>\n\t\tpublic async Task<IStorableChild> CreateCopyOfAsync(IStorableChild itemToCopy, bool overwrite = default, CancellationToken cancellationToken = default)\n\t\t{\n\t\t\tif (itemToCopy is IFile sourceFile)\n\t\t\t{\n\t\t\t\tvar copiedFile = await CreateFileAsync(itemToCopy.Name, overwrite, cancellationToken);\n\t\t\t\tawait sourceFile.CopyContentsToAsync(copiedFile, cancellationToken);\n\n\t\t\t\treturn copiedFile;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tthrow new NotSupportedException(\"Copying folders is not supported.\");\n\t\t\t}\n\t\t}\n\n\t\t/// <inheritdoc/>\n\t\tpublic async Task<IStorableChild> MoveFromAsync(IStorableChild itemToMove, IModifiableFolder source, bool overwrite = default, CancellationToken cancellationToken = default)\n\t\t{\n\t\t\tusing var ftpClient = GetFtpClient();\n\t\t\tawait ftpClient.EnsureConnectedAsync(cancellationToken);\n\n\t\t\tvar newItem = await CreateCopyOfAsync(itemToMove, overwrite, cancellationToken);\n\t\t\tawait source.DeleteAsync(itemToMove, cancellationToken);\n\n\t\t\treturn newItem;\n\t\t}\n\n\t\t/// <inheritdoc/>\n\t\tpublic async Task<IChildFile> CreateFileAsync(string desiredName, bool overwrite = default, CancellationToken cancellationToken = default)\n\t\t{","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/files-community/Files/blob/68c68a58d4d6a5f7197e07c8fff85cfd4279c78b/src/Files.App.Storage/Ftp/FtpStorageFolder.cs#L92-L128","documentation":"Thrown by FtpStorageFolder.CreateCopyOfAsync when the item to copy is not an IFile (i.e. it is an IFolder). The FTP storage implementation only supports byte-stream copies of files via CopyContentsToAsync; it has no recursive directory copy. MoveFromAsync internally delegates to CreateCopyOfAsync, so attempting to move a folder into an FTP folder hits the same path.","triggerScenarios":"Calling CreateCopyOfAsync on an FtpStorageFolder with an IStorableChild that is an IFolder (not IFile). Also triggered transitively by MoveFromAsync when the source item is a folder, since MoveFromAsync calls CreateCopyOfAsync then DeleteAsync.","commonSituations":"Drag-and-drop or copy/move UI passing a directory to an FTP destination; cross-provider copy where the source folder is not decomposed into individual file copies by the caller; generic copy code that treats files and folders uniformly without a branch.","solutions":["Enumerate the folder contents yourself and copy each child file individually, recreating subfolders via CreateFolderAsync.","Type-check the source before calling: only invoke CreateCopyOfAsync when itemToCopy is IFile; otherwise fall back to a manual recursive copy.","Surface a user-facing message that FTP folder copy is unsupported rather than letting the NotSupportedException propagate.","For cross-provider moves, avoid MoveFromAsync for folders; implement copy-then-delete manually."],"exampleFix":"// before\nawait destFolder.CreateCopyOfAsync(folderItem, overwrite, ct);\n\n// after\nif (folderItem is IFile file)\n    await destFolder.CreateCopyOfAsync(file, overwrite, ct);\nelse if (folderItem is IFolder srcFolder)\n    await CopyFolderRecursively(srcFolder, (IModifiableFolder)destFolder, ct);\nelse\n    throw new NotSupportedException();","handlingStrategy":"type-guard","validationCode":"// Only call CreateCopyOfAsync for files; handle folders separately.\nif (itemToCopy is not IFile)\n    throw new InvalidOperationException($\"Cannot copy {itemToCopy?.Name}: FTP folder copy is unsupported.\");\nvar copy = await ftpFolder.CreateCopyOfAsync(itemToCopy, overwrite, ct);","typeGuard":"static bool IsFtpCopyable(IStorableChild item) => item is IFile;","tryCatchPattern":"try { await dest.CreateCopyOfAsync(item, overwrite, ct); }\ncatch (NotSupportedException) when (item is not IFile)\n{ /* fall back to recursive folder copy */ }","preventionTips":["Branch on IFile vs IFolder before invoking FTP copy/move.","Do not route folder moves through MoveFromAsync; it delegates to the unsupported CreateCopyOfAsync.","Document at the UI layer that FTP directory copy is unsupported."],"tags":["ftp","storage","copy","notsupported"],"backgroundTag":null,"analyzedSha":"68c68a58d4d6a5f7197e07c8fff85cfd4279c78b","analyzedAt":"2026-08-13T10:34:54.614Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}