Hello Spring team,

First of all, I just wanted to thank you guys for the release of 4.0.0-M1. We are very excited for the final 4.0.0 version.

Just wanted to reach out with an issue for 4.0.0-M1 + Java 24 + GraalVM native image. I understand 4.0.0-M1 is just a milestone version. I also understand Spring Boot is being modularized in this version. But I managed to have a 100% reproducible result. I tried to make it as minimal as possible.

I have a very small 4 classes reproducible:

This is just the main class, nothing here.

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class QuestionApplication {

    public static void main(final String[] args) {
        SpringApplication.run(QuestionApplication.class);
    }

}

This is a class that deals with open telemetry tracing. I suspect the issue might be from here? But I do not have the technical capabilities to prove this.

package org.example;

import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class TracingConfiguration {

    @Bean
    public SpanExporter ptlpGrpcSpanExporter() {
        return OtlpGrpcSpanExporter.builder().setEndpoint("https://otlp-gateway-prod-us-central-0.grafana.net/otlp/v1/traces").addHeader("Authorization", "Basic ODYDA9").build(); //fake
    }

}

This is just a Reactor Kafka consumer configuration

package org.example;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.observation.ObservationRegistry;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.kafka.receiver.KafkaReceiver;
import reactor.kafka.receiver.MicrometerConsumerListener;
import reactor.kafka.receiver.ReceiverOptions;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Configuration
public class ConsumerConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerConfiguration.class);

    @Bean
    public KafkaReceiver<String, String> kafkaReceiver(final MeterRegistry meterRegistry, final ObservationRegistry observationRegistry, final SslBundles sslBundles) {
        final Map<String, Object> properties = new ConcurrentHashMap<>();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "bootstrapServer");
        properties.put(ConsumerConfig.CLIENT_ID_CONFIG, "consumerGroup");
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "consumerGroup");
        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        final ReceiverOptions<String, String> receiverOptions = ReceiverOptions.create(properties);
        return KafkaReceiver.create(receiverOptions

                .consumerListener(new MicrometerConsumerListener(meterRegistry))
                .withObservation(observationRegistry)
                .subscription(Collections.singleton("topicSource")));
    }

}

This is just the main service class, which does nothing but consumes messages in a reactive way with observability.

package org.example;

import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Mono;
import reactor.kafka.receiver.KafkaReceiver;
import reactor.kafka.receiver.observation.KafkaReceiverObservation;
import reactor.kafka.receiver.observation.KafkaRecordReceiverContext;

import java.util.Locale;
import java.util.function.Function;

@Service
public final class MyService implements CommandLineRunner {

    private final KafkaReceiver<String, String> kafkaReceiver;
    private final ObservationRegistry observationRegistry;

    public MyService(final KafkaReceiver<String, String> receiver, final ObservationRegistry registry) {
        this.kafkaReceiver = receiver;
        this.observationRegistry = registry;
    }

    @Override
    public void run(final String... args) {
        kafkaReceiver.receiveAutoAck().concatMap(Function.identity()).flatMap(this::transform).subscribe();
    }

    private Mono<String> transform(final ConsumerRecord<String, String> consumerRecord) {
        final var receiverObservation = KafkaReceiverObservation.RECEIVER_OBSERVATION.start(null, KafkaReceiverObservation.DefaultKafkaReceiverObservationConvention.INSTANCE, () -> new KafkaRecordReceiverContext(consumerRecord, "abc", "def"), observationRegistry);
        return Mono.just(consumerRecord)
                .map(one -> one.value().toUpperCase(Locale.ROOT))
                .tap(Micrometer.observation(observationRegistry))
                .doOnTerminate(receiverObservation::stop)
                .doOnError(receiverObservation::error)
                .contextWrite(context -> context.put(ObservationThreadLocalAccessor.KEY, receiverObservation));
    }

}

The most important file is this 4.0.0-M1 pom file. As you can see in this pom file, I added the modularized Spring Boot.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.0-M1</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>aotissue</artifactId>
    <version>1.1</version>

    <properties>
        <java.version>24</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-kafka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-opentelemetry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-webclient</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-core-micrometer</artifactId>
        </dependency>
        <dependency>
            <groupId>io.opentelemetry</groupId>
            <artifactId>opentelemetry-exporter-otlp</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-tracing-bridge-otel</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-otlp</artifactId>
        </dependency>

        <dependency>
            <groupId>io.projectreactor.kafka</groupId>
            <artifactId>reactor-kafka</artifactId>
            <version>1.3.23</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-context</artifactId>
            <version>4.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.github.loki4j</groupId>
            <artifactId>loki-logback-appender</artifactId>
            <version>2.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp-jvm</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <image>
                        <env>
                            <BP_JVM_VERSION>24</BP_JVM_VERSION>
                        </env>
                    </image>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>confluent</id>
            <url>https://packages.confluent.io/maven/</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

</project>

Now, running this Maven command mvn -U -Pnative spring-boot:build-image -DskipTests to generate the native image, the build works fine.

However, at run time, reprodubile 100%, there is this error trace

Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop'
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:414)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:400)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:592)
    at java.base@24.0.1/java.lang.Iterable.forEach(Iterable.java:75)
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:370)
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:306)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:997)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621)
    at org.springframework.boot.web.server.reactive.context.ReactiveWebServerApplicationContext.refresh(ReactiveWebServerApplicationContext.java:67)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1344)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1333)
    at org.example.QuestionApplication.main(QuestionApplication.java:13)
    at java.base@24.0.1/java.lang.invoke.LambdaForm$DMH/sa346b79c.invokeStaticInit(LambdaForm$DMH)
Caused by: java.lang.ExceptionInInitializerError
    at java.base@24.0.1/java.lang.Class.ensureInitialized(DynamicHub.java:658)
    at java.base@24.0.1/java.lang.Class.ensureInitialized(DynamicHub.java:658)
    at java.base@24.0.1/java.lang.Class.ensureInitialized(DynamicHub.java:658)
    at java.base@24.0.1/java.lang.Class.ensureInitialized(DynamicHub.java:658)
    at io.netty.util.internal.PlatformDependent.newFixedMpmcQueue(PlatformDependent.java:1177)
    at io.netty.buffer.AdaptivePoolingAllocator.createSharedChunkQueue(AdaptivePoolingAllocator.java:238)
    at io.netty.buffer.AdaptivePoolingAllocator.access$600(AdaptivePoolingAllocator.java:85)
    at io.netty.buffer.AdaptivePoolingAllocator$MagazineGroup.<init>(AdaptivePoolingAllocator.java:369)
    at io.netty.buffer.AdaptivePoolingAllocator.createMagazineGroupSizeClasses(AdaptivePoolingAllocator.java:211)
    at io.netty.buffer.AdaptivePoolingAllocator.<init>(AdaptivePoolingAllocator.java:182)
    at io.netty.buffer.AdaptiveByteBufAllocator.<init>(AdaptiveByteBufAllocator.java:56)
    at io.netty.buffer.AdaptiveByteBufAllocator.<init>(AdaptiveByteBufAllocator.java:51)
    at io.netty.buffer.AdaptiveByteBufAllocator.<init>(AdaptiveByteBufAllocator.java:47)
    at io.netty.buffer.ByteBufUtil.<clinit>(ByteBufUtil.java:90)
    at io.netty.buffer.ByteBufAllocator.<clinit>(ByteBufAllocator.java:24)
    at io.netty.channel.DefaultChannelConfig.<init>(DefaultChannelConfig.java:60)
    at io.netty.channel.epoll.EpollChannelConfig.<init>(EpollChannelConfig.java:48)
    at io.netty.channel.epoll.EpollServerChannelConfig.<init>(EpollServerChannelConfig.java:42)
    at io.netty.channel.epoll.EpollServerSocketChannelConfig.<init>(EpollServerSocketChannelConfig.java:34)
    at io.netty.channel.epoll.EpollServerSocketChannel.<init>(EpollServerSocketChannel.java:59)
    at io.netty.channel.epoll.EpollServerSocketChannel.<init>(EpollServerSocketChannel.java:45)
    at reactor.netty.resources.DefaultLoopEpoll.getChannel(DefaultLoopEpoll.java:58)
    at reactor.netty.resources.LoopResources.onChannel(LoopResources.java:243)
    at reactor.netty.tcp.TcpResources.onChannel(TcpResources.java:251)
    at reactor.netty.transport.TransportConfig.lambda$connectionFactory$1(TransportConfig.java:277)
    at reactor.netty.transport.TransportConnector.doInitAndRegister(TransportConnector.java:279)
    at reactor.netty.transport.TransportConnector.bind(TransportConnector.java:89)
    at reactor.netty.transport.ServerTransport.lambda$bind$0(ServerTransport.java:122)
    at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61)
    at reactor.core.publisher.Mono.subscribe(Mono.java:4571)
    at reactor.core.publisher.Mono.block(Mono.java:1801)
    at reactor.netty.transport.ServerTransport.bindNow(ServerTransport.java:157)
    at reactor.netty.transport.ServerTransport.bindNow(ServerTransport.java:142)
    at org.springframework.boot.reactor.netty.NettyWebServer.startHttpServer(NettyWebServer.java:171)
    at org.springframework.boot.reactor.netty.NettyWebServer.start(NettyWebServer.java:115)
    at org.springframework.boot.web.server.reactive.context.WebServerManager.start(WebServerManager.java:55)
    at org.springframework.boot.web.server.reactive.context.WebServerStartStopLifecycle.start(WebServerStartStopLifecycle.java:41)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:411)
    ... 15 more
Caused by: java.lang.RuntimeException: java.lang.NoSuchFieldException: producerIndex
    at io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess.fieldOffset(UnsafeAccess.java:111)
    at io.netty.util.internal.shaded.org.jctools.queues.MpmcArrayQueueProducerIndexField.<clinit>(MpmcArrayQueue.java:51)
    ... 53 more
Caused by: java.lang.NoSuchFieldException: producerIndex
    at java.base@24.0.1/java.lang.Class.checkField(DynamicHub.java:1151)
    at java.base@24.0.1/java.lang.Class.getDeclaredField(DynamicHub.java:1293)
    at io.netty.util.internal.shaded.org.jctools.util.UnsafeAccess.fieldOffset(UnsafeAccess.java:107)
    ... 54 more
jdktwentyfive@jdktwentyfive:~/IdeaProjects/springbootfouris

I can confirm that using Spring Boot 3.5.x, the same code will work.

May I ask what is the root cause of this issue?

Thank you for your time.

springbootfourissue.zip