I'm experiencing a problem when running my Spring Boot 3.2 application as a JAR with spring.threads.virtual.enabled=true. Under certain conditions, the following error is triggered:

reactor.core.scheduler.Schedulers        : Scheduler worker in group VirtualThreads failed with an uncaught exception

java.lang.NoClassDefFoundError: reactor/core/Exceptions
        at reactor.core.scheduler.Schedulers.handleError(Schedulers.java:1180) ~[reactor-core-3.6.0.jar!/:3.6.0]
        at reactor.core.scheduler.ThreadPerTaskBoundedElasticScheduler$SchedulerTask.run(ThreadPerTaskBoundedElasticScheduler.java:1018) ~[reactor-core-3.6.0.jar!/:3.6.0]
        at java.base/java.lang.VirtualThread.run(VirtualThread.java:309) ~[na:na]
Caused by: java.lang.ClassNotFoundException: reactor.core.Exceptions
        at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445) ~[na:na]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593) ~[na:na]
        at org.springframework.boot.loader.net.protocol.jar.JarUrlClassLoader.loadClass(JarUrlClassLoader.java:104) ~[spring-cloud-2023-test-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at org.springframework.boot.loader.launch.LaunchedClassLoader.loadClass(LaunchedClassLoader.java:91) ~[spring-cloud-2023-test-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
        at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[na:na]
        ... 3 common frames omitted

Upon debugging, I discovered that the actual class that could not be found is "reactor.core.publisher.MonoIgnoreElements$IgnoreElementsSubscriber", even though this class does indeed exist in the classpath. This leads me to suspect a class loading issue.

Comment From: wilkinsona

This may be another symptom of https://github.com/spring-projects/spring-boot/issues/38611. Could you please try 3.2.1-SNAPSHOT, available from https://repo.spring.io/snapshot, and let us know if it helps?

Comment From: honhimW

Okay, I will try the 3.2.1-SNAPSHOT later and let you know the results. Also, I looked at the issue link you provided, where you mentioned trying to preload using Class#forName. I have already tried this, and even though the required class can be found during debugging, the program still reports the same error when running.

Comment From: honhimW

This may be another symptom of #38611. Could you please try 3.2.1-SNAPSHOT, available from https://repo.spring.io/snapshot, and let us know if it helps? @wilkinsona

Well, it seems that upgrading to 3.2.1-SNAPSHOT doesn’t solve the problem... Actually, I’m not quite sure if this problem is caused by Spring Boot, because it could also be Spring Cloud or Project Reactor. So, I hope to get your help. I have created a sample project here to reproduce the error: https://github.com/honhimW/spring-boot-issues-38719.

Comment From: wilkinsona

Thanks for the sample. It appears to be more complex than needed as service discovery shouldn't be involved in the HTTP request handling. Please strip things down to the bare minimum that's required to reproduce the problem. If Spring Cloud Gateway is required, please hardcode it's routing if possible, rather than relying on service discovery and load balancing.

Comment From: honhimW

@wilkinsona I apologize for providing such a complex sample. However, I’m unable to reproduce the error in a simpler way, such as by hardcoding the route or customizing a service instance provider’s Bean or mocking a service instance’s CacheManager. For a specific instance, please refer to the branch: https://github.com/honhimW/spring-boot-issues-38719/tree/without-discovery.😥

Comment From: honhimW

Additionally, If I set the configuration in my build.gradle as follows, the error will not occur:

bootJar {
    loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
}

Comment From: honhimW

Well, I've simplified the sample for reproduction, but it still need gateway and loadbalancer. README.md

Comment From: philwebb

@honhimW I still suspect that this is #38611. I notice from the sample that although you're using 3.2.1-SNAPSHOT in your project, you're still using Spring Boot 3.2.0 for the Spring Boot Gradle plugin. It's the plugin that contributes the classes we think we've fixed.

Could you please try upgrading the plugin version to see if that fixes the issue. You'll probably also need to update settings.gradle with the following:

pluginManagement {
  repositories {
    maven { url 'https://repo.spring.io/milestone' }
    maven { url 'https://repo.spring.io/snapshot' }
    gradlePluginPortal()
  }
}

Comment From: honhimW

@philwebb Great! It indeed was an issue with the plugin version. But may I ask why this problem only occurs in version 3.2.0 when virtual-thread is enabled? I’ve looked at the related code fix records and it seems there is no relevant content.

By the way, regarding this issue, should I close it myself or leave it to you?

Comment From: wilkinsona

Thanks for confirming that this works with 3.2.1-SNAPSHOT. Enabling virtual threads changes the classes that are loaded and when they are loaded. It may also affect if and when threads are interrupted. One or both of these would be enough to trigger the problem that #38611 has fixed.