graal
graal copied to clipboard
[GR-65669] [Native Image] Build fails with GraalVM 24, but works with GraalVM 17
Describe the Issue
I've opened an issue first on Spring Boot, but seems the issue is related with GraalVM. Here is the issue: https://github.com/spring-projects/spring-boot/issues/45676#issuecomment-2915660689
After upgrading the dependencies the build started to fail with:
2689 | [INFO] [creator] Fatal error: java.lang.NoClassDefFoundError: reactor/core/publisher/Mono
2690 | [INFO] [creator] at java.base/java.lang.Class.getDeclaredMethods0(Native Method)
2691 | [INFO] [creator] at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3035)
2692 | [INFO] [creator] at java.base/java.lang.Class.getDeclaredMethod(Class.java:2422)
2693 | [INFO] [creator] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.reflect.ReflectionDataBuilder.lambda$registerMethodLookup$0(ReflectionDataBuilder.java:500)
2694 | [INFO] [creator] at org.graalvm.nativeimage.builder/com.oracle.svm.hosted.reflect.ReflectionDataBuilder.lambda$runConditionalInAnalysisTask$1(ReflectionDataBuilder.java:184)
2695 | [INFO] [creator] at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.executeCommand(CompletionExecutor.java:166)
2696 | [INFO] [creator] at org.graalvm.nativeimage.pointsto/com.oracle.graal.pointsto.util.CompletionExecutor.lambda$executeService$0(CompletionExecutor.java:152)
Which is something that shouldn't be used at all, since I don't have that class on the classpath.
Like it's said on the other issue, with GraalVM 17, the build works, so seems an issue related to GraalVM.
During my investigation running the agent, I see that spring boot uses multiple times the method Class.forName in order to check if the reactor package is present. From my perspective seems that GraalVM is trying to register all the classes that during the run with the agent were called with Class.forName, but if that returns a class not found exception, the hint shouldn't be generated for the native image.
Using the latest version of GraalVM can resolve many issues.
- [x] I tried with the latest version of GraalVM.
GraalVM Version
java 24.0.1 2025-04-15 Java(TM) SE Runtime Environment Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01) Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 24.0.1+9.1 (build 24.0.1+9-jvmci-b01, mixed mode, sharing)
Operating System and Version
Windows -> But build with docker paketobuildpacks/builder-jammy-tiny
Build Command
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<env>
<BP_JVM_VERSION>24</BP_JVM_VERSION>
<BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
<BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
--initialize-at-build-time=com.google.protobuf.RuntimeVersion
</BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
<BP_SPRING_AOT_ENABLED>true</BP_SPRING_AOT_ENABLED>
<BP_JVM_CDS_ENABLED>true</BP_JVM_CDS_ENABLED>
<BP_MAVEN_ACTIVE_PROFILES>native</BP_MAVEN_ACTIVE_PROFILES>
</env>
<!-- <builder>paketobuildpacks/builder-jammy-tiny</builder>-->
<name>${artifactPath}:latest</name>
<tags>
<tag>${artifactPath}:${commitSha}</tag>
</tags>
</image>
<excludes>
<exclude>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
</exclude>
<exclude>
<groupId>org.immutables</groupId>
<artifactId>value</artifactId>
</exclude>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
<exclude>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
Expected Behavior
To build successfully and not try to register Mono class for reflection. Since was never on the classpath, and is not used inside my app.
Actual Behavior
2689 | [INFO] [creator] Fatal error: java.lang.NoClassDefFoundError: reactor/core/publisher/Mono
Steps to Reproduce
- You can use from the linked issue the example pom, or the reproducer zip file.
- Native build (direct or inside a docker container)
Additional Context
No response
Build Log Output and Error Messages
No response
This error easy to reproduce is super difficult to diagnose. The best I had was by adding
io.projectreactor:reactor-core dependency and -H:AbortOnTypeReachable=reactor.core.publisher.Mono which gave me the following error message: "Type reactor.core.publisher.Mono is marked as reachable str: Is used by annotation of element registered for reflection".
So I think one of the classes referenced by the generated reflect-config.json has an annotation referencing Mono and that breaks the native compilation. At minimum GraalVM should print more context about the faulty entry in the logs, maybe even be lenient in this case and not break.
Hi @fabianiacob,
Thank you for reaching out to us about this. Could you please share a concise reproducer using a github repo with us? (I'm afraid I can't download the zip file you shared) alongside the steps needed to encounter the issue. Thank you.
Hello @selhagani , thank you for working on it. Sure, I've just created the repo https://github.com/fabianiacob/spring-test-native
FYI: I've tried building with GraalVM 21, and also works.
If a bit more minimal of a sample would help the GraalVM team, my colleague @jonatan-ivanov made https://github.com/jonatan-ivanov/micrometer-tracing-gh-1096, as we had the same issue reported to us in https://github.com/micrometer-metrics/tracing/issues/1096. It works with GraalVM 21 but not with GraalVM 24. What is the best way of figuring out from where exactly the native image build is determining the Mono type is reachable? I could potentially make an even more minimal sample if it would be helpful.
Just a guess on the root cause, but in https://github.com/micrometer-metrics/tracing/issues/1096#issuecomment-2983229260 I noticed ReactiveSecurityContextHolderThreadLocalAccessor is loaded by a ServiceLoader and has Mono as part of its signature. Is it possible this is causing Mono to be considered reachable on GraalVM 24 while it wasn't on GraalVM 21?
Bonjour @loicottet
Est-ce que l'exemple que l'équipe Micrometer a donné te permet d'avancer dans la résolution de ce petit hic?
This issue has been fixed by https://github.com/oracle/graal/pull/10482 and will be part of GraalVM 25.0.