Description
Overview
There are various circumstances that can cause the cleanup of a temporary directory to fail. Some of these errors may indicate open or leaking file handles, issues in the implementation being tested, or problems in the test itself. Others may occur only occasionally on slow filesystems, race-conditions in asynchronous process and so on.
Currently, a test encountering such a problem will fail with something like:
org.junit.platform.commons.JUnitException: Failed to close extension context
org.junit.platform.commons.JUnitException: Failed to close extension context
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.io.IOException: Failed to delete temp directory C:\Windows\TEMP\junit-6301891027881835454. The following paths could not be deleted (see suppressed exceptions for details): <root>, ws
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at java.base/java.util.stream.SortedOps$RefSortingSink.end(SortedOps.java:395)
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.base/java.util.stream.Sink$ChainedReference.end(Sink.java:258)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
... 2 more
Suppressed: java.nio.file.DirectoryNotEmptyException: C:\Windows\TEMP\junit-6301891027881835454
at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:272)
at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:105)
at java.base/java.nio.file.Files.delete(Files.java:1152)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2828)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2882)
... 13 more
Suppressed: java.nio.file.DirectoryNotEmptyException: C:\Windows\TEMP\junit-6301891027881835454\ws
at java.base/sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:272)
at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:105)
at java.base/java.nio.file.Files.delete(Files.java:1152)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2828)
at java.base/java.nio.file.Files.walkFileTree(Files.java:2882)
... 13 more
In my opinion, this is unfortunate and could also be considered a regression compared to JUnit 4. In JUnit 4, it was possible to work around these kinds of issues using TemporaryFolder#assureDeletion
, which allowed ignoring failed deletion of the temporary directory.
As of now, there is no way to bypass these issues in JUnit 5 unless you define @TempDir(cleanup = CleanupMode.NEVER)
or avoid using @TempDir
altogether - both of which seem undesirable.
See #4549 for a working proposal.
Activity
ascopes commentedon May 24, 2025
I can reproduce the same issue(s). It seems in my case to be related to the fact Windows does not appear to always immediately close file handles after Java requests them to be closed. I cannot reproduce on my Linux or MacOS builds, just Windows, and this is very spurious.
Pipeline reproducing this: https://github.com/ascopes/protobuf-maven-plugin/actions/runs/15225268219/job/42826508202?pr=674
Test case reproducing this: https://github.com/ascopes/protobuf-maven-plugin/blob/43d19dbe0c6cb98bfb4477d5672b5f159a211565/protobuf-maven-plugin/src/test/java/io/github/ascopes/protobufmavenplugin/fs/UriResourceFetcherTest.java#L135
In my case, I have observed that backing off for a few dozen milliseconds when this occurs and retrying often fixes the issue, so perhaps retry handling is needed in this case with a backoff in the case that the resource is still in use? The example above was from using
@ParameterizedTest
but I have seen it on regular@Test
cases as well... and it is always on Windows.For now, ignoring the errors would be much appreciated, as it is resulting in my builds being unstable and I'm having to repeatedly rerun builds that take several minutes to complete.
ascopes commentedon May 24, 2025
Is it possible to label this as a bug in the meantime?
Scope TempDir to test class (junit-team/junit-framework#4567)
sormuras commentedon May 24, 2025
No, it's not a bug of JUnit per-se.
Non-deletable files (on Windows) usually hint at a resource leak or a non-closed file descriptor in the code under test or the test code itself. Make sure that every file, directory stream, ..., any IO-related resource is closed after usage. IIRC, JUnit tries to release already closed file handles by calling
System,.gc()
at some point in the cleanup process, in order to help with some cases on Windows.22 remaining items