quarkusio/quarkus · error · UnsupportedOperationException

io_uring is not supported with Netty 4.2

Error message

io_uring is not supported with Netty 4.2

What it means

Pulsar's native-image substitutions stub out the legacy Netty io_uring incubator classes because they are incompatible with Netty 4.2. At native runtime, if the legacy IOUringEventLoop is somehow instantiated and its run() executes, it throws UnsupportedOperationException instead of doing real io_uring work.

Source

Thrown at extensions/smallrye-reactive-messaging-pulsar/runtime/src/main/java/io/quarkus/pulsar/runtime/graal/PulsarSubstitutions.java:20

import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;

/**
 * Pulsar 4.2.x depends on netty-incubator-transport-native-io_uring:0.0.26.Final
 * which was compiled against Netty 4.1.x. Netty 4.2 removed
 * {@code PlatformDependent.getIntVolatile(long)} causing native image linking
 * failures. These substitutions replace the methods that reference the missing
 * API so GraalVM can link the io_uring classes. The substituted methods are
 * never called at runtime because io_uring is opt-in
 * ({@code -Dpulsar.enableUring=true}) and Linux-only.
 */
@TargetClass(className = "io.netty.incubator.channel.uring.IOUringEventLoop")
final class Target_IOUringEventLoop {

    @Substitute
    protected void run() {
        throw new UnsupportedOperationException("io_uring is not supported with Netty 4.2");
    }

    @Substitute
    protected void cleanup() {
        throw new UnsupportedOperationException("io_uring is not supported with Netty 4.2");
    }
}

@TargetClass(className = "io.netty.incubator.channel.uring.Native")
final class Target_IOUringNative {

    @Substitute
    static Target_RingBuffer createRingBuffer(int ringSize) {
        throw new UnsupportedOperationException("io_uring is not supported with Netty 4.2");
    }

    @Substitute
    static Target_RingBuffer createRingBuffer(int ringSize, int flags) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the netty-transport-native-epoll/io_uring-incubator legacy wiring and let Netty 4.2's built-in io_uring (io.netty.transport.io_uring) be used
  2. Drop explicit IOUringEventLoopGroup / preferNative selection from client config
  3. Pin Netty to 4.1.x if the incubator io_uring transport is strictly required

Example fix

// before
EventLoopGroup group = new IOUringEventLoopGroup();
// after
EventLoopGroup group = new NioEventLoopGroup(); // or Netty 4.2 io_uring transport
Defensive patterns

Strategy: fallback

Validate before calling

// Detect legacy io_uring incubator usage at startup
Class.forName("io.netty.incubator.channel.uring.IOUringEventLoop");
if (isNativeImage() && nettyVersion() != null && nettyVersion().startsWith("4.2")) {
    log.warn("Legacy io_uring incubator detected with Netty 4.2 in native mode; falling back to NIO");
}

Try / catch

try {
    eventLoopGroup = new IOUringEventLoopGroup();
} catch (Throwable t) {
    if (t instanceof UnsupportedOperationException) {
        eventLoopGroup = new NioEventLoopGroup();
    } else {
        throw t;
    }
}

Prevention

When it happens

Trigger: Running a Pulsar-client application as a native image with Netty 4.2 on the classpath while code explicitly selects the io_uring event loop (e.g. -Dio.netty.transport.preferNative=true or explicit IOUringEventLoopGroup usage).

Common situations: Migrating Pulsar apps to Netty 4.2 where the old io_uring incubator dependency is still present and still referenced by transport configuration.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/4a52c79754a90feb. Report an issue: GitHub.