grpc/grpc-java · error · RuntimeException

Cannot load EpollServerSocketChannel

Error message

Cannot load EpollServerSocketChannel

What it means

gRPC-netty loads io.netty.channel.epoll.EpollServerSocketChannel reflectively when configuring an epoll-based server. This RuntimeException wraps the ClassNotFoundException raised when the epoll transport classes are absent from the classpath. It is thrown only after the code has already assumed epoll is available, so it signals a missing/mispackaged epoll dependency.

Source

Thrown at netty/src/main/java/io/grpc/netty/Utils.java:437

          .forName("io.netty.channel.epoll.EpollEventLoopGroup").asSubclass(EventLoopGroup.class)
          .getConstructor(Integer.TYPE, ThreadFactory.class);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Cannot load EpollEventLoopGroup", e);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("EpollEventLoopGroup constructor not found", e);
    }
  }

  // Must call when epoll is available
  private static Class<? extends ServerChannel> epollServerChannelType() {
    try {
      Class<? extends ServerChannel> serverSocketChannel =
          Class
              .forName("io.netty.channel.epoll.EpollServerSocketChannel")
              .asSubclass(ServerChannel.class);
      return serverSocketChannel;
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Cannot load EpollServerSocketChannel", e);
    }
  }

  private static EventLoopGroup createEpollEventLoopGroup(
      int parallelism,
      ThreadFactory threadFactory) {
    checkState(EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR != null, "Epoll is not available");

    try {
      return EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR
          .newInstance(parallelism, threadFactory);
    } catch (Exception e) {
      throw new RuntimeException("Cannot create Epoll EventLoopGroup", e);
    }
  }

  private static ChannelFactory<ServerChannel> nioServerChannelFactory() {
    return new ChannelFactory<ServerChannel>() {

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add io.netty:netty-transport-native-epoll with the matching linux classifier and Netty version to the server's runtime classpath
  2. Verify the packaged artifact (jar/image) contains io/netty/channel/epoll/EpollServerSocketChannel and the native epoll .so
  3. Use a consistent Netty version (netty-bom) so grpc-netty's reflective lookups resolve
  4. Only enable the epoll transport on Linux; otherwise rely on the default NIO transport

Example fix

// before (build.gradle)
implementation 'io.grpc:grpc-netty-shaded:1.x' // or grpc-netty without epoll natives

// after
implementation 'io.grpc:grpc-netty:1.x'
runtimeOnly 'io.netty:netty-transport-native-epoll:4.1.100.Final:linux-x86_64'
runtimeOnly 'io.netty:netty-transport-native-unix-common:4.1.100.Final:linux-x86_64'
Defensive patterns

Strategy: fallback

Validate before calling

static boolean epollServerTransportReady() {
  try {
    Class.forName("io.netty.channel.epoll.EpollServerSocketChannel");
    return true;
  } catch (ClassNotFoundException e) {
    return false;
  }
}

Type guard

static Class<? extends io.netty.channel.ServerChannel> serverChannelTypeOrNio() {
  try {
    return io.grpc.netty.Utils.EPOLL_SERVER_CHANNEL_TYPE;
  } catch (Throwable t) {
    return io.netty.channel.socket.nio.NioServerSocketChannel.class;
  }
}

Try / catch

try {
  return NettyServerBuilder.forPort(port)
      .channelType(io.grpc.netty.Utils.EPOLL_SERVER_CHANNEL_TYPE)
      .build();
} catch (RuntimeException e) {
  if (e.getCause() instanceof ClassNotFoundException) {
    return NettyServerBuilder.forPort(port) // default NIO transport
        .build();
  }
  throw e;
}

Prevention

When it happens

Trigger: Starting a gRPC server on the epoll transport, which calls Utils.epollServerChannelType() to resolve the server channel class, while io.netty:netty-transport-native-epoll is missing from the runtime classpath.

Common situations: Deploying gRPC servers to Linux with epoll transport enabled (explicitly or via default detection) but the slim Docker image or shaded jar omitted netty-transport-native-epoll; Netty version mismatch removing io.netty.channel.epoll classes; non-Linux deployment accidentally taking the epoll path.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/8e9bcf10397687ef. Report an issue: GitHub.