grpc/grpc-java · error · RuntimeException

Cannot load EpollEventLoopGroup

Error message

Cannot load EpollEventLoopGroup

What it means

gRPC-netty reflectively loads io.netty.channel.epoll.EpollEventLoopGroup and looks up its (int, ThreadFactory) constructor. This RuntimeException wraps ClassNotFoundException, meaning the epoll transport classes are missing from the classpath at the moment the epoll event loop group is created.

Source

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

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

  // Must call when epoll is available
  private static Constructor<? extends EventLoopGroup> epollEventLoopGroupConstructor() {
    try {
      return Class
          .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);
    }
  }

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add io.netty:netty-transport-native-epoll with a linux classifier matching your platform and Netty version
  2. Verify with dependency:tree that only one consistent Netty version is present and it contains EpollEventLoopGroup
  3. If epoll transport was configured explicitly (transport(...)/epoll option), remove that setting to fall back to the default NIO event loop group
  4. Ensure the native epoll library (.so) is packaged — the classes alone are insufficient if the native binding is also stripped

Example fix

// before
NettyServerBuilder.forPort(8080)
    .transport(NettyServerBuilder.EPOLL) // requires epoll on classpath
    .build();

// after — add to build:
// implementation 'io.netty:netty-transport-native-epoll:4.1.x:linux-x86_64'
NettyServerBuilder.forPort(8080)
    .transport(NettyServerBuilder.EPOLL)
    .build();
Defensive patterns

Strategy: fallback

Validate before calling

static boolean epollEventLoopAvailable() {
  try {
    Class.forName("io.netty.channel.epoll.EpollEventLoopGroup")
        .getConstructor(Integer.TYPE, ThreadFactory.class);
    return true;
  } catch (ClassNotFoundException | NoSuchMethodException e) {
    return false;
  }
}

Type guard

static boolean canCreateEpollEventLoopGroup() {
  try {
    Class<?> c = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup");
    return c.getConstructor(Integer.TYPE, ThreadFactory.class) != null;
  } catch (Throwable t) {
    return false;
  }
}

Try / catch

try {
  builder.eventLoopGroup(io.grpc.netty.Utils.createEpollEventLoopGroup(cores, factory));
} catch (RuntimeException e) {
  if (e.getCause() instanceof ClassNotFoundException) {
    builder.eventLoopGroup(new NioEventLoopGroup(cores, factory));
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Creating the epoll event loop group via Utils.epollEventLoopGroupConstructor() when building an epoll-based channel/server (Utils.createEpollEventLoopGroup) while io.netty:netty-transport-native-epoll is not on the runtime classpath.

Common situations: Using EpollEventLoopGroupConfigured transport options (NettyServerBuilder/NettyChannelBuilder with epoll transport) on Linux where the native epoll dependency was not bundled; Netty version drift causing io.netty.channel.epoll.EpollEventLoopGroup to disappear from the jar set; shaded jars missing epoll classes.

Related errors


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