How to Delete a Folder in C# (With Files, Subfolders or Only Empty Ones)

To delete a folder in C#, call Directory.Delete(path, recursive: true). It removes the folder and every file and subfolder inside it. Drop the true and it only deletes an empty folder.

Directory.Delete(@"C:\DotNetDemo\DeleteFolderDemo\Projects\2025", recursive: true);

In this tutorial, I’ll work through each C# delete folder case I’ve run into: empty and non-empty folders, missing paths, clearing a folder but keeping it, removing only empty subfolders, and read-only or locked files.

The sample data is an old project archive for a Denver architecture firm. Each program rebuilds its own folders under C:\DotNetDemo\DeleteFolderDemo\Projects, so it’s safe to rerun.

C# Delete Folder: Empty vs. Non-Empty with Directory.Delete

This program creates an empty Scratch folder and a LoDo-Lofts folder with one file in it. Then it deletes both, first without and then with recursive: true:

string root = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2025";
string empty = Path.Combine(root, "Scratch");
string full = Path.Combine(root, "LoDo-Lofts");
Directory.CreateDirectory(empty);
Directory.CreateDirectory(full);
File.WriteAllText(Path.Combine(full, "site-plan.pdf"), "sample");

Directory.Delete(empty);
Console.WriteLine($"Scratch exists: {Directory.Exists(empty)}");

try { Directory.Delete(full); }
catch (IOException ex) { Console.WriteLine($"IOException: {ex.Message}"); }

Directory.Delete(full, recursive: true);
Console.WriteLine($"LoDo-Lofts exists: {Directory.Exists(full)}");

Output:

Scratch exists: False
IOException: The directory is not empty. : 'C:\DotNetDemo\DeleteFolderDemo\Projects\2025\LoDo-Lofts'.
LoDo-Lofts exists: False
VS Code running DeleteFolder.cs: Directory.Delete removes the empty Scratch folder, throws IOException The directory is not empty for LoDo-Lofts, then recursive: true deletes it
Directory.Delete on an empty folder, a non-empty folder, and with recursive: true

The empty folder went away with the one-argument call. The folder holding site-plan.pdf threw IOException: The directory is not empty, and the second call with recursive: true deleted it along with the file.

Keep in mind the delete is permanent; nothing goes to the Recycle Bin. Per Microsoft Learn, a recursive delete also doesn’t follow symbolic links or mount points, so it won’t empty their targets.

I ran every program as a .NET 10 file-based app with dotnet run File.cs. You can also create a console application in Visual Studio Code and paste the code into Program.cs.

Check If the Directory Exists: DirectoryNotFoundException and DirectoryInfo.Delete

Unlike File.Delete, Directory.Delete throws when the path isn’t there. Here’s that error, the Directory.Exists guard, and the DirectoryInfo version:

string missing = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2019";
try
{
    Directory.Delete(missing, recursive: true);
}
catch (DirectoryNotFoundException ex)
{
    Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}

if (Directory.Exists(missing))                     // the guard most code needs
    Directory.Delete(missing, recursive: true);
Console.WriteLine("With the Exists guard: no exception");

var archive = new DirectoryInfo(@"C:\DotNetDemo\DeleteFolderDemo\Projects\2024");
archive.CreateSubdirectory(@"Union-Station\Renders");
File.WriteAllText(Path.Combine(archive.FullName, "Union-Station", "notes.txt"), "sample");
archive.Delete(recursive: true);
archive.Refresh();                                 // Exists is cached until Refresh
Console.WriteLine($"DirectoryInfo.Delete(true) -> 2024 exists: {archive.Exists}");

Output:

DirectoryNotFoundException: Could not find a part of the path 'C:\DotNetDemo\DeleteFolderDemo\Projects\2019'.
With the Exists guard: no exception
DirectoryInfo.Delete(true) -> 2024 exists: False

The missing 2019 folder raised DirectoryNotFoundException. Wrapping the call in if (Directory.Exists(...)) skips it quietly, which is what most cleanup code wants.

DirectoryInfo.Delete(true) does the same job as the static method. Call Refresh() before you read Exists again, because the object caches it. It’s the reverse of how you create a folder if it does not exist in C#.

Delete All Files and Subfolders but Keep the Folder

Sometimes the folder itself has to stay, like an Inbox that other code writes to. Delete its files, then its subfolders:

var inbox = new DirectoryInfo(@"C:\DotNetDemo\DeleteFolderDemo\Projects\Inbox");
inbox.CreateSubdirectory(@"Cherry-Creek-Bids\Drafts");     // recreate sample content
File.WriteAllText(Path.Combine(inbox.FullName, "client-email.txt"), "sample");
File.WriteAllText(Path.Combine(inbox.FullName, "Cherry-Creek-Bids", "bid.pdf"), "sample");

foreach (FileInfo file in inbox.EnumerateFiles())
    file.Delete();
foreach (DirectoryInfo dir in inbox.EnumerateDirectories())
    dir.Delete(recursive: true);

inbox.Refresh();
Console.WriteLine($"Inbox exists: {inbox.Exists}, items left: {inbox.EnumerateFileSystemInfos().Count()}");

Output:

Inbox exists: True, items left: 0

The Inbox still exists with zero items left. If you only need the files gone and want the subfolders kept, drop the second foreach loop, or see my guide on how to delete files from a folder in C#.

How Do I Delete Only Empty Folders in C#?

To remove empty subfolders and keep anything holding files, visit the deepest folders first. Then a parent that becomes empty gets deleted in the same pass:

string root = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2025";
string[] tree = [@"Highlands-Library\Drawings",
                 @"Highlands-Library\Renders",
                 @"RiNo-Studio\Photos\Raw", "Sloan-Lake"];
foreach (string d in tree)
    Directory.CreateDirectory(Path.Combine(root, d));
File.WriteAllText(Path.Combine(root, tree[0], "plan.pdf"), "");
var all = SearchOption.AllDirectories;

foreach (string d in Directory.GetDirectories(root, "*", all)
             .OrderByDescending(p => p.Count(c => c == '\\')))
{
    if (Directory.EnumerateFileSystemEntries(d).Any()) continue;
    Directory.Delete(d);
    Console.WriteLine("Deleted: " + Path.GetRelativePath(root, d));
}
foreach (string d in Directory.GetDirectories(root, "*", all))
    Console.WriteLine("Kept:    " + Path.GetRelativePath(root, d));

Output:

Deleted: RiNo-Studio\Photos\Raw
Deleted: Highlands-Library\Renders
Deleted: RiNo-Studio\Photos
Deleted: RiNo-Studio
Deleted: Sloan-Lake
Kept:    Highlands-Library
Kept:    Highlands-Library\Drawings
VS Code running DeleteEmptyFolders.cs: Raw, Renders, Photos, RiNo-Studio and Sloan-Lake are deleted as empty folders while Highlands-Library and Drawings are kept
Deleting empty folders bottom-up: parents that end up empty go too

RiNo-Studio only held empty folders, so it went after Photos\Raw and Photos. Highlands-Library stayed because Drawings holds plan.pdf.

Sorting by the number of \ separators puts the deepest paths first, much like you’d sort an array in C#. GetDirectories returns the full array before the loop starts, so you never delete a folder mid-enumeration.

Why Does Directory.Delete Throw UnauthorizedAccessException on a Read-Only File?

A read-only file anywhere in the tree stops a recursive delete. Here’s the error and the fix:

string root = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2023";
string contract = Path.Combine(root, "signed-contract.pdf");
Directory.CreateDirectory(root);
if (File.Exists(contract))
    File.SetAttributes(contract, FileAttributes.Normal);
File.WriteAllText(contract, "sample");
File.SetAttributes(contract, FileAttributes.ReadOnly);

try { Directory.Delete(root, recursive: true); }
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)
{ Console.WriteLine($"{ex.GetType().Name}: {ex.Message}"); }

foreach (string f in Directory.GetFiles(root, "*", SearchOption.AllDirectories))
    File.SetAttributes(f, FileAttributes.Normal);
Directory.Delete(root, recursive: true);
Console.WriteLine($"2023 exists now: {Directory.Exists(root)}");

Output:

UnauthorizedAccessException: Access to the path 'signed-contract.pdf' is denied.
2023 exists now: False
VS Code running ReadOnlyFolder.cs: Directory.Delete recursive throws UnauthorizedAccessException for a read-only file, then succeeds after clearing the attributes
A read-only file blocks the recursive delete until you clear its attributes

On .NET 10 the run threw UnauthorizedAccessException, while the Learn page lists IOException for a read-only file. That’s why I catch both with an exception filter, as explained in my try-catch guide for C#.

Setting every file back to FileAttributes.Normal fixed it. FileAttributes is a flags enum, which I cover in my post on enums in C#.

Delete a Folder When a File Inside Is Locked

If another handle has a file open, for example a Revit model someone left open, the recursive delete fails with IOException:

string root = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2022";
string model = Path.Combine(root, "Red-Rocks-Pavilion.rvt");
Directory.CreateDirectory(root);
File.WriteAllText(model, "sample");

using (var open = File.Open(model, FileMode.Open,
           FileAccess.Read, FileShare.None))
{
    try { Directory.Delete(root, recursive: true); }
    catch (IOException ex) { Console.WriteLine($"IOException: {ex.Message}"); }
}

// The using block closed the handle, so this works
Directory.Delete(root, recursive: true);
Console.WriteLine($"2022 exists now: {Directory.Exists(root)}");

Output:

IOException: The process cannot access the file 'Red-Rocks-Pavilion.rvt' because it is being used by another process.
2022 exists now: False
VS Code running LockedFolder.cs: Directory.Delete throws IOException because Red-Rocks-Pavilion.rvt is being used by another process, then works after the stream closes
A file opened with FileShare.None blocks Directory.Delete until the handle closes

As soon as the using block closed the stream, the same call worked. In your own code, dispose every stream you open, as covered in Dispose and Finalize in C#.

Learn also notes that a folder open in File Explorer may refuse to delete, and you can’t delete your app’s current working directory.

Send a Folder to the Recycle Bin Instead of Deleting It

Directory.Delete has no Recycle Bin option, but Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory does. It ships with .NET 10, so a C# app can use it without a package:

using Microsoft.VisualBasic.FileIO;

string oldArchive = @"C:\DotNetDemo\DeleteFolderDemo\Projects\2021";
if (Directory.Exists(oldArchive))
{
    FileSystem.DeleteDirectory(oldArchive, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
    Console.WriteLine("Sent 2021 to the Recycle Bin");
}

I built this with dotnet build but didn’t run it, since it would fill a real Recycle Bin. Treat it as Windows-only, and per Learn, the recycle option isn’t supported in non-interactive apps like Windows Services.

Which C# Delete Directory Method Should You Use?

TaskUse this
Delete an empty folderDirectory.Delete(path)
Delete a folder with files and subfoldersDirectory.Delete(path, true)
Folder might not existif (Directory.Exists(path)) first
Empty the folder, keep itDelete each file, then each subfolder
Delete only empty subfoldersDeepest first, delete if no entries
Read-only files insideSet FileAttributes.Normal, then delete
Recycle Bin (Windows)FileSystem.DeleteDirectory(..., RecycleOption.SendToRecycleBin)

My rule of thumb: guard with Directory.Exists, catch IOException and UnauthorizedAccessException, and only point a recursive delete at a folder your app owns.

If you’re building a folder-cleanup tool, these pair well with it:

Frequently Asked Questions

How do I delete a folder that is not empty in C#?

Pass true as the second argument: Directory.Delete(path, true). Without it, a folder with files or subfolders throws IOException with the message “The directory is not empty.”

Does Directory.Delete throw if the folder doesn’t exist?

Yes, it throws DirectoryNotFoundException. Check Directory.Exists(path) first. See the Directory.Delete reference on Microsoft Learn for the full exception list.

Is there an async version of Directory.Delete?

No, .NET has no Directory.DeleteAsync. If a big tree would freeze a UI, run it on a background thread with await Task.Run(() => Directory.Delete(path, true));.

Does Directory.Delete send folders to the Recycle Bin?

No, it deletes permanently. On Windows, use FileSystem.DeleteDirectory from Microsoft.VisualBasic.FileIO with RecycleOption.SendToRecycleBin.

What’s the difference between Directory.Delete and DirectoryInfo.Delete?

They do the same thing. Directory.Delete takes a path string, while DirectoryInfo.Delete works on an object you already have; call Refresh() before checking Exists afterward.