grpc/grpc-java · error · RuntimeException

Cannot load EpollDomainSocketChannel

Error message

Cannot load EpollDomainSocketChannel

What it means

gRPC-netty loads io.netty.channel.epoll.EpollDomainSocketChannel reflectively to support Unix domain socket channels. This RuntimeException wraps the ClassNotFoundException raised when the epoll transport classes are not on the classpath. The caller is expected to invoke this only when epoll is available, so failing here means the epoll dependency is missing despite that assumption.

Source

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

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

  // 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() {

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Add io.netty:netty-transport-native-epoll with the appropriate linux classifier to the runtime classpath
  2. Confirm the UDS address is only used on Linux with epoll support present; otherwise fall back to TCP/loopback
  3. Align Netty versions (dependency:tree) so io.netty.channel.epoll.EpollDomainSocketChannel exists at the expected version
  4. Check packaging rules (shade excludes, jlink, image slimming) are not stripping io.netty.channel.epoll.* or the native .so

Example fix

// before
ManagedChannel ch = NettyChannelBuilder.forAddress("unix", "/var/run/srv.sock").build(); // throws if epoll missing

// after — ensure classpath has:
// implementation 'io.netty:netty-transport-native-epoll:4.1.x:linux-x86_64'
ManagedChannel ch = NettyChannelBuilder.forAddress("unix", "/var/run/srv.sock")
    .channelType(io.grpc.netty.Utils.EPOLL_DOMAIN_SOCKET_CHANNEL_TYPE)
    .build();
Defensive patterns

Strategy: fallback

Validate before calling

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

Type guard

static boolean canUseUnixDomainSocket() {
  return "Linux".equalsIgnoreCase(System.getProperty("os.name")) && epollDomainSocketSupported();
}

Try / catch

try {
  return NettyChannelBuilder.forAddress("unix", sockPath);
} catch (RuntimeException e) {
  if (e.getCause() instanceof ClassNotFoundException) {
    return NettyChannelBuilder.forAddress("localhost", tcpPort); // fall back to TCP
  }
  throw e;
}

Prevention

When it happens

Trigger: Resolving the domain socket channel type via Utils.epollDomainSocketChannelType() (used when the target address is a unix:/// UDS address) while io.netty:netty-transport-native-epoll is absent from the runtime classpath.

Common situations: Connecting to a server over a Unix domain socket (NettyChannelBuilder.forAddress(UnixDomainSocketAddress...) or 'unix:' target) on Linux without the netty-transport-native-epoll dependency; slimmed container images or shaded jars that omit the epoll classes/natives.

Related errors


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