files-community/Files · error · NotSupportedException

Moving to recycle bin is not supported.

Error message

Moving to recycle bin is not supported.

What it means

ZipStorageFile.DeleteAsync throws NotSupportedException("Moving to recycle bin is not supported.") only when deleting the archive root (Path == containerPath) with a non-permanent delete option (i.e. StorageDeleteOption.Default, which sends to recycle bin) and there is no backingFile. The recycle bin API needs a real filesystem shell item; a path-only zip root without a backing file cannot be recycled. Permanent deletes (and any delete that has a backingFile) are handled.

Source

Thrown at src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs:567

		public override IAsyncAction DeleteAsync() => DeleteAsync(StorageDeleteOption.Default);
		public override IAsyncAction DeleteAsync(StorageDeleteOption option)
		{
			return AsyncInfo.Run((cancellationToken) => SafetyExtensions.WrapAsync(async () =>
			{
				if (Path == containerPath)
				{
					if (backingFile is not null)
					{
						await backingFile.DeleteAsync();
					}
					else if (option == StorageDeleteOption.PermanentDelete)
					{
						PInvoke.DeleteFileFromApp(Path);
					}
					else
					{
						throw new NotSupportedException("Moving to recycle bin is not supported.");
					}
				}
				else
				{
					var index = await FetchZipIndex();
					if (index < 0)
					{
						return;
					}
					using (var ms = new MemoryStream())
					{
						await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.Read))
						{
							SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append };
							compressor.CustomParameters.Add("cu", "on");
							compressor.SetFormatFromExistingArchive(archiveStream);
							await compressor.ModifyArchiveAsync(archiveStream, new Dictionary<int, string>() { { index, null } }, Credentials.Password, ms);
						}

View on GitHub (pinned to 68c68a58d4)

Solutions

  1. Use StorageDeleteOption.PermanentDelete when deleting the path-only zip root, or provide a backingFile so backingFile.DeleteAsync() is used.
  2. Construct the ZipStorageFile via the overload that takes a BaseStorageFile backingFile so recycle-bin deletes route through the backing file.
  3. At the call site, detect zip-root + no backing file and switch to permanent delete or resolve the StorageFile first.

Example fix

// before
await zipRootFile.DeleteAsync(); // default == recycle bin, throws for path-only root

// after
await zipRootFile.DeleteAsync(StorageDeleteOption.PermanentDelete);
Defensive patterns

Strategy: validation

Validate before calling

bool CanRecycle(BaseStorageFile f) => !(f is ZipStorageFile zsf && zsf.Path == /*containerPath*/ /*&& no backingFile*/);

Type guard

static bool IsZipRootByPath(BaseStorageFile f) => f is ZipStorageFile zsf && zsf.Path == typeof(ZipStorageFile).GetField("containerPath", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.GetValue(f) as string;

Try / catch

try { await f.DeleteAsync(); }
catch (NotSupportedException) { await f.DeleteAsync(StorageDeleteOption.PermanentDelete); }

Prevention

When it happens

Trigger: Deleting a ZipStorageFile that is the archive container itself (Path == containerPath), with no backingFile, using the default delete option (StorageDeleteOption.Default == recycle bin).

Common situations: A user presses Delete (not Shift+Delete) on a .zip file that was opened directly by path (no StorageFile backing), so the code requests a recycle-bin delete.

Related errors


AI-assisted analysis of files-community/Files@68c68a58d4 (2026-08-13). Data as JSON: /api/errors/677ebc31ad4b11a5. Report an issue: GitHub.