42

I am trying to delete a directory recursively with rm -Force -Recurse somedirectory, I get several "The directory is not empty" errors. If I retry the same command, it succeeds.

Example:

PS I:\Documents and Settings\m\My Documents\prg\net> rm -Force -Recurse .\FileHelpers
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data\RunTime\_svn: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (_svn:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data\RunTime: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (RunTime:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests\Data: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (Data:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\FileHelpers.Tests: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (FileHelpers.Tests:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs\nunit\_svn: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (_svn:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs\nunit: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (nunit:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers\Libs: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (Libs:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot remove item I:\Documents and Settings\m\My Documents\prg\net\FileHelpers: The directory is not empty.
At line:1 char:3
+ rm <<<<  -Force -Recurse .\FileHelpers
    + CategoryInfo          : WriteError: (I:\Documents an...net\FileHelpers:DirectoryInfo) [Remove-Item], IOException
    + FullyQualifiedErrorId : RemoveFileSystemItemIOError,Microsoft.PowerShell.Commands.RemoveItemCommand
PS I:\Documents and Settings\m\My Documents\prg\net> rm -Force -Recurse .\FileHelpers
PS I:\Documents and Settings\m\My Documents\prg\net>

Of course, this doesn't happen always. Also, it doesn't happen only with _svn directories, and I don't have a TortoiseSVN cache or anything like that so nothing is blocking the directory.

Any ideas?

11 Answers 11

42

help Remove-Item says:

The Recurse parameter in this cmdlet does not work properly.

and

Because the Recurse parameter in this cmdlet is faulty, the command uses the Get-Childitem cmdlet to get the desire d files, and it uses the pipeline operator to pass them to the Remove-Item cmdlet.

and proposes this alternative as an example:

get-childitem * -include *.csv -recurse | remove-item

So you should pipe get-childitem -recurse into remove-item.

8
  • Thanks. Just found this thread from 2006: vistax64.com/powershell/… looks like Microsoft is not really interested in fixing this. Commented Nov 9, 2010 at 20:00
  • 2
    At least the documentation says it doesn't work. Commented Nov 20, 2011 at 4:12
  • 8
    I had to put both -force -recurse flags for Remove-Item, otherwise it kept me prompting "please confirm" Get-ChildItem -Path $Destination -Recurse | Remove-Item -force -recurse Commented Jan 17, 2012 at 6:37
  • 2
    does not work for me, i still get the error
    – Blub
    Commented Dec 3, 2014 at 12:42
  • 1
    The known problem mentioned in the help relates only to combining -Recurse with -Include - it doesn't explain the OP's errors. Your sample command removes files from a directory tree, whereas the OP is looking to remove a nonempty directory as a whole. The true cause of the problem still exists as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 and is explained in this bug report.
    – mklement
    Commented Nov 28, 2018 at 15:01
25

@JamesCW: The problem still exists in PowerShell 4.0

I tried another workaround and it worked: use cmd.exe:

&cmd.exe /c rd /s /q $somedirectory
6
  • 1
    Good old rd /s /q!
    – JamesCW
    Commented May 5, 2016 at 18:24
  • I've tried every variation of Get-ChildItem; retry loops; calling iisreset before deleting and nothing seems to work reliably. I'm gonna try this one, even though when I first saw it I balked at having DOS inside my Powershell... Commented Aug 23, 2016 at 8:04
  • 2
    Unfortunately, rd /s fails intermittently too (though seemingly less often than Remove-Item): github.com/Microsoft/console/issues/309
    – mklement
    Commented Nov 27, 2018 at 20:45
  • It doesn't like the slash by the c for me. Do you have to precede it by powershell -command and single quote the cmd.exe part? I get "You must provide a value expression following the '/' operator." "Unexpected token 'c' in expression or statement. It's the same with powershell -command in front of it. Does the / need escaping?
    – Michele
    Commented Aug 22, 2019 at 15:05
  • 1
    This worked for me when PowerShell Remove-Item did not (including -recurse -force). However @mklement answer explains how most solutions only mitigate the problem, but some can completely fix it.
    – RichVel
    Commented Jan 28, 2020 at 7:40
13

Update: Starting with (at least) Windows 10 version 20H2 (I don't know that Windows Server version and build that corresponds to; run winver.exe to check your version and build), the DeleteFile Windows API function now exhibits synchronous behavior, which implicitly solves the problems with PowerShell's Remove-Item and .NET's System.IO.File.Delete / System.IO.Directory.Delete (but, curiously, not with cmd.exe's rd /s).


The existing answers mitigate the problem, so that it occurs less frequently, but they don't address the root cause, which is why failures can still occur.

Remove-Item -Recurse is unexpectedly asynchronous, ultimately because the Windows API methods for file and directory removal are inherently asynchronous and Remove-Item doesn't account for that.

This intermittently, unpredictably manifests in one of two ways:

  • Your case: Removing a nonempty directory itself can fail, if removal of a subdirectory or file in it hasn't completed yet by the time an attempt is made to remove the parent directory.

  • Less commonly: Recreating a removed directory immediately after removal can fail, because the removal may not have completed yet by the time re-creation is attempted.

The problem not only affects PowerShell's Remove-Item, but also cmd.exe's rd /s as well as .NET's [System.IO.Directory]::Delete():

As of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 / cmd.exe 10.0.17134.407 / .NET Framework 4.7.03056, .NET Core 2.1, neither Remove-Item, nor rd /s, nor [System.IO.Directory]::Delete() work reliably, because they fail to account for the asynchronous behavior of the Windows API file/directory-removal functions:

For a custom PowerShell function that provides a reliably synchronous workaround, see this SO answer.

1
  • When handling files where removal is certain: while($true) { if ( (Remove-Item [...] *>&1) -ne $null) { Start-Sleep 0.5 } else { break } }
    – Farway
    Commented Jul 25, 2019 at 22:55
8

ETA 20181217: PSVersion 4.0 and later will still fail in some circumstances, see alternate answer by Mehrdad Mirreza, and bug report filed by mklement

mklement provides a Proof of Concept solution at this SO answer, as the bug is awaiting an official fix

The new version of PowerShell (PSVersion 4.0) has resolved this issue entirely and Remove-Item "targetdirectory" -Recurse -Force works without any timing problems.

You can check your version by running $PSVersiontable from within the ISE or PowerShell prompt. 4.0 is the version that ships with Windows 8.1 and Server 2012 R2, and it can be installed on previous versions of Windows as well.

5
  • 5
    Still occurs for me in PowerShell 4.0
    – ajbeaven
    Commented Nov 3, 2015 at 19:39
  • 12
    Still occurs in PowerShell v5 !!!!!11!!1!1!!! Commented Nov 25, 2016 at 8:46
  • @RichardHauer well now I'm just confused
    – JamesCW
    Commented Dec 4, 2016 at 4:04
  • 2
    @JamesCW I've converted to the rd version. Aside from actually working, it's about 3x faster Commented Dec 4, 2016 at 4:28
  • 3
    The problem has not been fixed as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report. While rd /s may fail less often, it too is broken - see this bug report.
    – mklement
    Commented Nov 28, 2018 at 14:52
5

Gosh. Lots of answers. I honestly prefer this one over all of them. It's super simple, complete, readable, and works on any Windows machine. It uses .NET's (reliable) recursive delete functionality and if it fails for some reason, it throws a proper exception that can be handled with a try/catch block.

$fullPath = (Resolve-Path "directory\to\remove").ProviderPath
[IO.Directory]::Delete($fullPath, $true)

Note that the Resolve-Path line is important because .NET is not aware of your current directory when resolving relative file paths. That's about the only gotcha I can think of.

3

The current answer won't actually delete a directory, just its children. Furthermore it will have problems with nested directories as it will again be trying to delete a directory before its contents. I wrote something to delete the files in the correct order, would still have the same problem though sometimes the directory would still be around afterward.

So, now I use something that will catch the exception, wait, and retry (3 times):

For now I'm using this:

function EmptyDirectory($directory = $(throw "Required parameter missing")) {

    if ((test-path $directory) -and -not (gi $directory | ? { $_.PSIsContainer })) {
        throw ("EmptyDirectory called on non-directory.");
    }

    $finished = $false;
    $attemptsLeft = 3;

    do {
        if (test-path $directory) {
            rm $directory -recurse -force
        }

        try {
            $null = mkdir $directory
            $finished = $true
        } 
        catch [System.IO.IOException] {
            Start-Sleep -Milliseconds 500
        }

        $attemptsLeft = $attemptsLeft - 1;
    } 
    while (-not $finished -and $attemptsLeft -gt 0)

    if (-not $finished) {
        throw ("Unable to clean and recreate directory " + $directory)
    }
}
2
  • 1
    This is good but I still had problems with it. If the mkdir command runs before the system completes the rm command it can throw a System.UnauthorizedAccessException with a FullyQualifiedErrorId of ItemExistsUnauthorizedAccessError. I.e., the directory hasn't been deleted by the OS yet (on my slow HDD). So that error needs to be caught too. And it's a non-terminating error, so the ErrorAction needs to be set to Stop. I also put the rm command in the try block too, just in cases there are transient IO errors when deleting. Commented May 26, 2016 at 14:02
  • I can't believe this even has to be done. Damn, Powershell sucks!
    – jcollum
    Commented Jul 12, 2017 at 16:20
3

To delete the directory and its contents takes two steps. First delete the contents, then the folder itself. Using the workaround for the faulty recursive remove item the solution would look like this:

Get-ChildItem -Path "$folder\\*" -Recurse | Remove-Item -Force -Recurse
Remove-Item $folder

This way you can remove the parent directory as well.

5
  • 1
    This is exactly what the accepted answer said. Do you have anything to add? Commented Dec 14, 2013 at 0:11
  • 1
    They are pointing out that the accepted answer does not delete the directory itself, hence it takes two steps. Commented May 2, 2014 at 15:01
  • 2
    The Remove-Item command your piping into has the same problem that originally stated. It might stumble on a directory item that is non-empty in the same way.
    – Dejan
    Commented Feb 26, 2016 at 12:16
  • @Dejan This directory could not still be empty if the first line of this code worked, could it? Commented Jun 7, 2016 at 14:22
  • 2
    While this may decrease the likelihood of failure, it can still fail, given that Remove-Item -Recurse is still involved. The underlying problem still exists as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report.
    – mklement
    Commented Nov 28, 2018 at 14:55
2

This is what I have working:

$Target = "c:\folder_to_delete"

Get-ChildItem -Path $Target -Recurse -force |
  Where-Object { -not ($_.psiscontainer) } |
   Remove-Item –Force

Remove-Item -Recurse -Force $Target

This first line deletes all files in the tree. The second deletes all the folders including the top.

1
  • 1
    While this may decrease the likelihood of failure, it can still fail, given that Remove-Item -Recurse is still involved. The underlying problem still exists as of Windows PowerShell v5.1 / PowerShell Core 6.2.0-preview.1 - see this bug report.
    – mklement
    Commented Nov 28, 2018 at 14:57
0

Take ownership of the files / directories first using Takeown.exe then delete

https://learn-powershell.net/2014/06/24/changing-ownership-of-file-or-folder-using-powershell/

0

I had this issue with a directory that would not delete. I found that one of the subfolders was corrupted and when I tried to move or rename that child dir I got an error message saying something about it being missing. I tried using rm -Force and got the same error as you did.

What worked for me was compressing the parent dir using 7-zip with the "Delete files after compression" option checked. Once it was compressed I was able to delete the zip file.

0

The solution above works for most of my scripts. But then I found some folders that the above solution didn't work on. What fixed it for me on the stubborn folder is robocopy /PURGE /PURGE: Delete dest files/folders that no longer exist in the source So if you make an empty fold, copy it to the destination folder with robocopy /PURGE. Robocopy will clean it out then you can del the folder with $output_path | Remove-Item -Recurse -force

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .