PowerShell Remove-Item not waiting PowerShell Remove-Item not waiting powershell powershell

PowerShell Remove-Item not waiting


Update: Starting with (at least [1]) 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).


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: recreating a removed directory immediately after removal can fail, because the removal may not have completed yet by the time re-creation is attempted.

  • More typically: Removing a nonempty directory itself can fail, if removal of a subdirectory or file hasn't completed yet by the time an attempt is made to remove the parent directory - this is demonstrated in the ServerFault answer marsze links to.

A potential workaround is to reuse an existing directory by emptying it - instead of deleting and recreating it.

However, emptying the directory with Get-ChildItem $OUT -Recurse | Remove-Item -Recurse is also susceptible to intermittent failures, though likely less often.

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 answer.


[1] I've personally verified that the issue is resolved in version 20H2, by running the tests in GitHub issue #27958 for hours without failure; this answer suggests that the problem was resolved as early as version 1909, starting with build 18363.657, but Dinh Tran finds that the issue is not resolved as of build 18363.1316 when removing large directory trees such as node_modules. I couldn't find any official information on the subject.


The Remove-Item command has a known issue.

Try this instead:

if (Test-Path $OUT) {     # if exists: empty contents and reuse the directory itself    Get-ChildItem $OUT -Recurse | Remove-Item -Recurse}else{    # else: create    New-Item -ItemType Directory -Path $OUT}

Note:

  • The Get-ChildItem command only finds non-hidden files and subdirectories, so emptying out the target directory may not be complete; to include hidden items too, add -Force.

  • Similarly, add -Force to -RemoveItem to force removal of files that have the read-only attribute set.

    • Without -Force, emptying may again be incomplete, but you'll get non-terminating errors in this case; if you want to treat them as terminating errors, add -ErrorAction Stop too.


For completeness' sake: You can also use the safe and fast .NET methods:

if ([System.IO.Directory]::Exists($OUT)) {    [System.IO.Directory]::Delete($OUT, $true)}[System.IO.Directory]::CreateDirectory($OUT)

Note:

Depending on where you get the value of $OUT you might want to convert it to a full path first to make sure the .NET methods remove the correct directory (see @mklement0`s comment):

$fullPath = Convert-Path $OUT