[Platform] Intermittent FileSystemAlreadyExistsException when build tool uses resources in JARs rather than plain files
The original context is a Scala project (build tool SBT) where following the upgrade to SBT 2, I faced this error intermittently:
DetailsWARNING: Error scanning files for URI jar:file:/home/runner/.cache/sbt/v2/cas/sha256-6a387a1afa16efdfb8dc71a8b673f22e5cb2115c97cbe8b1a89a399ce004014a-214786!/parametertypes
java.nio.file.FileSystemAlreadyExistsException
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:101)
at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:273)
at org.junit.platform.commons.util.CloseablePath.lambda$create$0(CloseablePath.java:52)
at org.junit.platform.commons.util.CloseablePath$ManagedFileSystem.<init>(CloseablePath.java:106)
at org.junit.platform.commons.util.CloseablePath.lambda$createForJarFileSystem$0(CloseablePath.java:75)
at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1932)
at org.junit.platform.commons.util.CloseablePath.createForJarFileSystem(CloseablePath.java:74)
at org.junit.platform.commons.util.CloseablePath.create(CloseablePath.java:62)
at org.junit.platform.commons.util.CloseablePath.create(CloseablePath.java:52)
at org.junit.platform.commons.util.DefaultClasspathScanner.walkFilesForUri(DefaultClasspathScanner.java:167)
at org.junit.platform.commons.util.DefaultClasspathScanner.findResourcesForUri(DefaultClasspathScanner.java:159)
at org.junit.platform.commons.util.DefaultClasspathScanner.lambda$findResourcesForUris$0(DefaultClasspathScanner.java:149)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1716)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:635)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:291)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:652)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:658)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:663)
at org.junit.platform.commons.util.DefaultClasspathScanner.findResourcesForUris(DefaultClasspathScanner.java:152)
at org.junit.platform.commons.util.DefaultClasspathScanner.scanForResourcesInPackage(DefaultClasspathScanner.java:108)
at org.junit.platform.commons.util.ReflectionUtils.findAllResourcesInPackage(ReflectionUtils.java:1036)
at org.junit.platform.commons.support.ResourceSupport.findAllResourcesInPackage(ResourceSupport.java:152)
at org.junit.platform.engine.support.discovery.ResourceContainerSelectorResolver.resolve(ResourceContainerSelectorResolver.java:61)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$0(EngineDiscoveryRequestResolution.java:161)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1693)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:147)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:588)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:574)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:683)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:191)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:128)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:94)
[error] Test parametertypes.RunParameterTypesTest failed: org.junit.platform.suite.engine.NoTestsDiscoveredException: Suite [parametertypes.RunParameterTypesTest] did not discover any tests, took 0.006s
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:85)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:150)
at io.cucumber.junit.platform.engine.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:32)
at io.cucumber.junit.platform.engine.FeaturesPropertyResolver.resolveSelectors(FeaturesPropertyResolver.java:52)
at io.cucumber.junit.platform.engine.CucumberTestEngine.discover(CucumberTestEngine.java:65)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:195)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:174)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:119)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:102)
at org.junit.platform.suite.engine.SuiteLauncher.discover(SuiteLauncher.java:58)
at org.junit.platform.suite.engine.SuiteTestDescriptor.discover(SuiteTestDescriptor.java:133)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:632)
at org.junit.platform.suite.engine.DiscoverySelectorResolver.discoverSuites(DiscoverySelectorResolver.java:40)
at org.junit.platform.suite.engine.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:48)
at org.junit.platform.suite.engine.SuiteTestEngine.discover(SuiteTestEngine.java:62)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:195)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:174)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:119)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:84)
at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:131)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$1(DefaultLauncher.java:124)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:121)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:100)
at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:57)
at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$0(InterceptingLauncher.java:46)
at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathAlignmentCheckingLauncherInterceptor.java:25)
at org.junit.platform.launcher.core.InterceptingLauncher.execute(InterceptingLauncher.java:45)
at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:57)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:76)
at com.github.sbt.junit.jupiter.internal.JupiterRunner$JupiterTaskExecutor.execute(JupiterRunner.java:227)
at com.github.sbt.junit.jupiter.internal.JupiterRunner$WithCustomProperties.execute(JupiterRunner.java:300)
at com.github.sbt.junit.jupiter.internal.JupiterRunner$JupiterTask.execute(JupiterRunner.java:131)
at sbt.TestRunner.runTest$1(TestFramework.scala:154)
at sbt.TestRunner.run(TestFramework.scala:170)
at sbt.TestFramework$.$anon$superArg$1$1$$anonfun$1$$anonfun$1(TestFramework.scala:317)
at sbt.TestFramework$.withContextLoader(TestFramework.scala:303)
at sbt.TestFramework$.$anon$superArg$1$1$$anonfun$1(TestFramework.scala:317)
at sbt.TestFunction.apply(TestFramework.scala:329)
at sbt.Tests$.$anonfun$8(Tests.scala:440)
at sbt.std.Transform$.sbt$std$Transform$$anon$1$$_$apply$$anonfun$1(Transform.scala:50)
at sbt.std.Transform$$anon$2.work(Transform.scala:70)
at sbt.std.Transform$$anon$2.work(Transform.scala:70)
at sbt.Execute.submit$$anonfun$1$$anonfun$1(Execute.scala:283)
at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
at sbt.Execute.work(Execute.scala:294)
at sbt.Execute.submit$$anonfun$1(Execute.scala:283)
at sbt.ConcurrentRestrictions$$anon$4.$anonfun$2(ConcurrentRestrictions.scala:269)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:75)
at sbt.CompletionService$$anon$2.call(CompletionService.scala:73)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:328)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
at java.base/java.lang.Thread.run(Thread.java:1474)Related run and code: https://github.com/cucumber/cucumber-jvm-scala/actions/runs/32169643804/job/95817434873?pr=434
Originally reported to SBT team (https://github.com/sbt/sbt/issues/9625) but they claim it's not a SBT issue but rather a JUnit one.
I believe the underlying root cause is that SBT 2 changed the way resources (and classes) are given to the test classpath: they are now provided in JARs (from ~/.cache/sbt/v2/cas/, as can be seen in the stacktrace) rather than plain files (like SBT 1.x).
When tests are run concurrently and access the same JAR, the exception above can be raised.
I spent a bit of time to build a reproduction repository, using SBT and Scala: https://github.com/gaeljw/sbt2-junit5-fsrace-repro. It was built with AI but I reviewed it and it's close to the actual call path I was hitting in my original project (cucumber-jvm-scala).
The high-level idea is that several "runners" @Suite (concurrently) have to scan the classpath for @Tests and when these tests are in a JAR file in the classpath, the FileSystemAlreadyExistsException can be hit. The exception is logged as a WARNING but has then for consequence that the runner fails with org.junit.platform.suite.engine.NoTestsDiscoveredException: Suite [consumertest.RunSharedTests] did not discover any tests.
Stack trace in the reproduction repo:
DetailsError scanning files for URI jar:file:/home/jourdang/.cache/sbt/v2/cas/sha256-a8940a97428e0c9599333c3a98649948f6ec3f4d619e9d7aa456474806b9c8ce-506072!/shared
java.nio.file.FileSystemAlreadyExistsException
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.newFileSystem(ZipFileSystemProvider.java:101)
at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:322)
at java.base/java.nio.file.FileSystems.newFileSystem(FileSystems.java:273)
at org.junit.platform.commons.util.CloseablePath.lambda$create$0(CloseablePath.java:52)
at org.junit.platform.commons.util.CloseablePath$ManagedFileSystem.<init>(CloseablePath.java:106)
at org.junit.platform.commons.util.CloseablePath.lambda$createForJarFileSystem$0(CloseablePath.java:75)
at java.base/java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1932)
at org.junit.platform.commons.util.CloseablePath.createForJarFileSystem(CloseablePath.java:74)
at org.junit.platform.commons.util.CloseablePath.create(CloseablePath.java:62)
at org.junit.platform.commons.util.CloseablePath.create(CloseablePath.java:52)
at org.junit.platform.commons.util.DefaultClasspathScanner.walkFilesForUri(DefaultClasspathScanner.java:167)
at org.junit.platform.commons.util.DefaultClasspathScanner.findClassesForUri(DefaultClasspathScanner.java:135)
at org.junit.platform.commons.util.DefaultClasspathScanner.lambda$findClassesForUris$0(DefaultClasspathScanner.java:125)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1716)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:635)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:291)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:652)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:658)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:663)
at org.junit.platform.commons.util.DefaultClasspathScanner.findClassesForUris(DefaultClasspathScanner.java:128)
at org.junit.platform.commons.util.DefaultClasspathScanner.scanForClassesInPackage(DefaultClasspathScanner.java:88)
at org.junit.platform.commons.util.ReflectionUtils.findAllClassesInPackage(ReflectionUtils.java:1029)
at org.junit.platform.commons.util.ReflectionUtils.findAllClassesInPackage(ReflectionUtils.java:1013)
at org.junit.platform.commons.support.ReflectionSupport.findAllClassesInPackage(ReflectionSupport.java:282)
at org.junit.platform.engine.support.discovery.ClassContainerSelectorResolver.resolve(ClassContainerSelectorResolver.java:58)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.lambda$resolve$0(EngineDiscoveryRequestResolution.java:161)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1693)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:147)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:588)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:574)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:683)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:191)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolve(EngineDiscoveryRequestResolution.java:128)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.resolveCompletely(EngineDiscoveryRequestResolution.java:94)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolution.run(EngineDiscoveryRequestResolution.java:85)
at org.junit.platform.engine.support.discovery.EngineDiscoveryRequestResolver.resolve(EngineDiscoveryRequestResolver.java:150)
at org.junit.platform.suite.engine.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:47)
at org.junit.platform.suite.engine.SuiteTestEngine.discover(SuiteTestEngine.java:62)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:195)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:174)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:119)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:102)
at org.junit.platform.suite.engine.SuiteLauncher.discover(SuiteLauncher.java:58)
at org.junit.platform.suite.engine.SuiteTestDescriptor.discover(SuiteTestDescriptor.java:133)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:186)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:214)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1939)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:570)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:560)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:153)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:176)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:265)
at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:632)
at org.junit.platform.suite.engine.DiscoverySelectorResolver.discoverSuites(DiscoverySelectorResolver.java:40)
at org.junit.platform.suite.engine.DiscoverySelectorResolver.resolveSelectors(DiscoverySelectorResolver.java:48)
at org.junit.platform.suite.engine.SuiteTestEngine.discover(SuiteTestEngine.java:62)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverEngineRoot(EngineDiscoveryOrchestrator.java:195)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discoverSafely(EngineDiscoveryOrchestrator.java:174)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:119)
at org.junit.platform.launcher.core.EngineDiscoveryOrchestrator.discover(EngineDiscoveryOrchestrator.java:84)
at org.junit.platform.launcher.core.DefaultLauncher.discover(DefaultLauncher.java:131)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$1(DefaultLauncher.java:124)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:121)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:100)
at org.junit.platform.launcher.core.DelegatingLauncher.execute(DelegatingLauncher.java:57)
at org.junit.platform.launcher.core.InterceptingLauncher.lambda$execute$0(InterceptingLauncher.java:46)
at org.junit.platform.launcher.core.ClasspathAlignmentCheckingLauncherInterceptor.intercept(ClasspathSource: junit-team/junit-framework