Cannot delete directory with Directory.Delete(path, true)

Viewed 282093

I'm using .NET 3.5, trying to recursively delete a directory using:

Directory.Delete(myPath, true);

My understanding is that this should throw if files are in use or there is a permissions problem, but otherwise it should delete the directory and all of its contents.

However, I occasionally get this:

System.IO.IOException: The directory is not empty.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)
    at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)
    ...

I'm not surprised that the method sometimes throws, but I'm surprised to get this particular message when recursive is true. (I know the directory is not empty.)

Is there a reason I'd see this instead of AccessViolationException?

27 Answers

Editor's note: Although this answer contains some useful information, it is factually incorrect about the workings of Directory.Delete. Please read the comments for this answer, and other answers to this question.


I ran into this problem before.

The root of the problem is that this function does not delete files that are within the directory structure. So what you'll need to do is create a function that deletes all the files within the directory structure then all the directories before removing the directory itself. I know this goes against the second parameter but it's a much safer approach. In addition, you will probably want to remove READ-ONLY access attributes from the files right before you delete them. Otherwise that will raise an exception.

Just slap this code into your project.

public static void DeleteDirectory(string target_dir)
{
    string[] files = Directory.GetFiles(target_dir);
    string[] dirs = Directory.GetDirectories(target_dir);

    foreach (string file in files)
    {
        File.SetAttributes(file, FileAttributes.Normal);
        File.Delete(file);
    }

    foreach (string dir in dirs)
    {
        DeleteDirectory(dir);
    }

    Directory.Delete(target_dir, false);
}

Also, for me I personally add a restriction on areas of the machine that are allowed to be deleted because do you want someone to call this function on C:\WINDOWS (%WinDir%) or C:\.

I had the very same problem under Delphi. And the end result was that my own application was locking the directory I wanted to delete. Somehow the directory got locked when I was writing to it (some temporary files).

The catch 22 was, I made a simple change directory to it's parent before deleting it.

I had a those weird permission problems deleting User Profile directories (in C:\Documents and Settings) despite being able to do so in the Explorer shell.

File.SetAttributes(target_dir, FileAttributes.Normal);
Directory.Delete(target_dir, false);

It makes no sense to me what a "file" operation does on a directory, but I know that it works and that's enough for me!

Is it possible you have a race condition where another thread or process is adding files to the directory:

The sequence would be:

Deleter process A:

  1. Empty the directory
  2. Delete the (now empty) directory.

If someone else adds a file between 1 & 2, then maybe 2 would throw the exception listed?

You don't have to create and extra method for recursivity or delete files inside folder extra. This all doing automatically by calling

DirectoryInfo.Delete();

Details is here.

Something like this works quite good:

  var directoryInfo = new DirectoryInfo("My directory path");
    // Delete all files from app data directory.

    foreach (var subDirectory in directoryInfo.GetDirectories())
    {
          subDirectory.Delete(true);// true set recursive paramter, when it is true delete sub file and sub folder with files too
    }

passing true as variable to delete method, will delete sub files and sub folder with files too.

The directory or a file in it is locked and cannot be deleted. Find the culprit who locks it and see if you can eliminate it.

It appears that having the path or subfolder selected in Windows Explorer is enough to block a single execution of Directory.Delete(path, true), throwing an IOException as described above and dying instead of booting Windows Explorer out to a parent folder and proceding as expected.

This is because of FileChangesNotifications.

It happens since ASP.NET 2.0. When you delete some folder within an app, it gets restarted. You can see it yourself, using ASP.NET Health Monitoring.

Just add this code to your web.config/configuration/system.web:

<healthMonitoring enabled="true">
  <rules>
    <add name="MyAppLogEvents" eventName="Application Lifetime Events" provider="EventLogProvider" profile="Critical"/>
  </rules>
</healthMonitoring>


After that check out Windows Log -> Application. What is going on:

When you delete folder, if there is any sub-folder, Delete(path, true) deletes sub-folder first. It is enough for FileChangesMonitor to know about removal and shut down your app. Meanwhile your main directory is not deleted yet. This is the event from Log:


enter image description here


Delete() didn't finish its work and because app is shutting down, it raises an exception:

enter image description here

When you do not have any subfolders in a folder that you are deleting, Delete() just deletes all files and that folder, app is getting restarted too, but you don't get any exceptions, because app restart doesn't interrupt anything. But still, you lose all in-process sessions, app doesn't response to requests when restarting, etc.

What now?

There are some workarounds and tweaks to disable this behaviour, Directory Junction, Turning Off FCN with Registry, Stopping FileChangesMonitor using Reflection (since there is no exposed method), but they all don't seem to be right, because FCN is there for a reason. It is looking after structure of your app, which is not structure of your data. Short answer is: place folders you want to delete outside of your app. FileChangesMonitor will get no notifications and your app will not be restarted every time. You will get no exceptions. To get them visible from the web there are two ways:

  1. Make a controller that handles incoming calls and then serves files back by reading from folder outside an app (outside wwwroot).

  2. If your project is big and performance is most important, set up separate small and fast webserver for serving static content. Thus you will leave to IIS his specific job. It could be on the same machine (mongoose for Windows) or another machine (nginx for Linux). Good news is you don't have to pay extra microsoft license to set up static content server on linux.

Hope this helps.

If your application's (or any other application's) current directory is the one you're trying to delete, it will not be an access violation error but a directory is not empty. Make sure it's not your own application by changing the current directory; also, make sure the directory is not open in some other program (e.g. Word, excel, Total Commander, etc.). Most programs will cd to the directory of the last file opened, which would cause that.

I´ve solved with this millenary technique (you can leave the Thread.Sleep on his own in the catch)

bool deleted = false;
        do
        {
            try
            {
                Directory.Delete(rutaFinal, true);                    
                deleted = true;
            }
            catch (Exception e)
            {
                string mensaje = e.Message;
                if( mensaje == "The directory is not empty.")
                Thread.Sleep(50);
            }
        } while (deleted == false);
Related