Skip to content

FileSystemAlreadyExistsException when build tool uses resources in JARs rather than plain files #6080

Closed

Description

@gaeljw

The original context is a Scala project (build tool SBT) where following the upgrade to SBT 2, I faced this error intermittently:

Details

WARNING: 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 (sbt/sbt#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:

Details

Error 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(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)


I can't definitely say if it's a JUnit issue but I wanted to raise it to you as you will likely understand this better than I do. Any feedback appreciated.


Sounds related to:

Activity

  1. added theissue type on Sep 19, 2026
  2. mpkorstanje commented on Sep 20, 2026

    @mpkorstanje
    Member

    I then asked AI (Claude) to help me understand this a bit more. Here's its findings

    This is a bit of a slop grenade.

    Could you fact check these findings to the point you no longer have to attribute them to Claude? I'm going to assume the reproducer is based on the same findings, but without you confirming them the findings are purely hypothetical.

    More importantly, it would be useful to know how SBT invokes JUnit and Cucumber otherwise we will indeed end up having to debug SBT. And for that a minimal reproducer with SBT would have been useful. 😉

  3. gaeljw commented on Sep 20, 2026

    @gaeljw
    Author

    Hey, nice to see you here @mpkorstanje :)

    That's fair feedback.

    I do confirm the fact that SBT 2 changed its behavior regarding putting resources in JARs rather than plain files in the test classpath.

    I spent a bit of time to build a closer reproduction repository, using SBT this time: 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:

    Error 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(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)
    

    (Original post updated as well)

  4. marcphilipp commented on Sep 20, 2026

    @marcphilipp
    Member

    @gaeljw Could you please try this workaround?

    Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
  5. mpkorstanje commented on Sep 20, 2026

    @mpkorstanje
    Member

    Hey, nice to see you here @mpkorstanje :)

    Small world 😄

    I do confirm the fact that SBT 2 changed its behavior regarding putting resources in JARs rather than plain files in the test classpath.

    Much appreciated!

  6. mpkorstanje commented on Sep 20, 2026

    @mpkorstanje
    Member
    Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars

    If that works it makes me wonder who's bug this is. To discover tests the JUnit Platform needs to be able to scan the classpath. And it needs to be able to do so in parallel if SBT runs JUnit in parallel. But that requires book keeping to keep track of the open file systems, which SBT also prevents by putting the concurrent instances of JUnit in separate class loaders.

    With those constraints I don't think anyone can reasonably do anything with any shared resource.

  7. 6 remaining items

  8. mpkorstanje commented on Sep 25, 2026

    @mpkorstanje
  9. self-assigned this
    on Oct 9, 2026
  10. changed the title [-][Platform] Intermittent FileSystemAlreadyExistsException when build tool uses resources in JARs rather than plain files[/-] [+]FileSystemAlreadyExistsException when build tool uses resources in JARs rather than plain files[/+] on Oct 9, 2026
  11. mpkorstanje commented on Oct 21, 2026

    @mpkorstanje
    Member

    So in the end it turns out that FileSystems.newFileSystem(Path, Map) behaves rather differently than FileSystems.newFileSystem(URI, Map). For the former the ZipFileSystemProvider will always open a new file system, the latter will cache the opened file system and refuse to open another when already open.

    Reproducer in JShell:

    Files.writeString(Path.of("a.txt"), "abc")
    
    /open TOOLING
    jar("--create", "--no-manifest", "--file", "a.jar", "a.txt")
    jar("--list", "--file", "a.jar")
    
    FileSystems.newFileSystem(Path.of("a.jar"), Map.of())
    FileSystems.newFileSystem(Path.of("a.jar"), Map.of()) // Works fine
    
    FileSystems.newFileSystem(URI.create("jar:" + Path.of("a.jar").toUri()), Map.of())
    FileSystems.newFileSystem(URI.create("jar:" + Path.of("a.jar").toUri()), Map.of()) // Throws exception.
    

    We're reworking the logic to use FileSystems.newFileSystem(Path, Map) in #6143.

  12. gaeljw commented on Oct 21, 2026

    @gaeljw
    Author

    Interesting! It's always "funny" to discover such behaviour differences from 2 apparently very similar methods 🤯

  13. added a commit that references this issue on Oct 23, 2026
    810d70b
  14. github-actions commented on Oct 23, 2026

    @github-actions

    Please assign a milestone to this issue or label it with type: task or type: question – or assign a status label and close it as not planned.

  15. added this to the 6.2.0-M1 milestone on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions