36

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?

10 Answers 10

36

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. Nov 9 '10 at 20:00
  • @mausch: See this more recent, but still unresolved, reference: Remove-Item -Recurse Nov 9 '10 at 22:21
  • 2
    At least the documentation says it doesn't work. Nov 20 '11 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 Jan 17 '12 at 6:37
  • 1
    does not work for me, i still get the error
    – Blub
    Dec 3 '14 at 12:42
21

@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
    May 5 '16 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... Aug 23 '16 at 8:04
  • 2
    Unfortunately, rd /s fails intermittently too (though seemingly less often than Remove-Item): github.com/Microsoft/console/issues/309
    – mklement
    Nov 27 '18 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
    Aug 22 '19 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
    Jan 28 '20 at 7:40
9

Update: Starting with Windows 10 version 1909, (at least) build 18363.657 (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
    Jul 25 '19 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
    Nov 3 '15 at 19:39
  • 11
    Still occurs in PowerShell v5 !!!!!11!!1!1!!! Nov 25 '16 at 8:46
  • @RichardHauer well now I'm just confused
    – JamesCW
    Dec 4 '16 at 4:04
  • 2
    @JamesCW I've converted to the rd version. Aside from actually working, it's about 3x faster Dec 4 '16 at 4:28
  • 2
    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
    Nov 28 '18 at 14:52
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. May 26 '16 at 14:02
  • I can't believe this even has to be done. Damn, Powershell sucks!
    – jcollum
    Jul 12 '17 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? Dec 14 '13 at 0:11
  • 1
    They are pointing out that the accepted answer does not delete the directory itself, hence it takes two steps. May 2 '14 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
    Feb 26 '16 at 12:16
  • @Dejan This directory could not still be empty if the first line of this code worked, could it? Jun 7 '16 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
    Nov 28 '18 at 14:55
3

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.

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
    Nov 28 '18 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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.