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

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

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

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

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?
| Task | Use this |
|---|---|
| Delete an empty folder | Directory.Delete(path) |
| Delete a folder with files and subfolders | Directory.Delete(path, true) |
| Folder might not exist | if (Directory.Exists(path)) first |
| Empty the folder, keep it | Delete each file, then each subfolder |
| Delete only empty subfolders | Deepest first, delete if no entries |
| Read-only files inside | Set 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.
Bijay Kumar is a renowned software engineer, accomplished author, and distinguished Microsoft Most Valuable Professional (MVP) specializing in SharePoint. With a rich professional background spanning over 15 years, Bijay has established himself as an authority in the field of information technology. He possesses unparalleled expertise in multiple programming languages and technologies such as ASP.NET, ASP.NET MVC, C#.NET, and SharePoint, which has enabled him to develop innovative and cutting-edge solutions for clients across the globe. Read more…