grpc/grpc-java · error · RuntimeException
ChannelOption(${optionName}) is not available
Error message
ChannelOption(${optionName}) is not available What it means
Utils.getEpollChannelOption reflectively reads a static ChannelOption field (e.g. EpollChannelOption.TCP_USER_TIMEOUT) from io.netty.channel.epoll.EpollChannelOption. If the class exists but the field lookup or access fails (missing in the Netty version, or access error), the exception is wrapped and rethrown with this message. It signals an incompatibility between the requested epoll channel option and the Netty version on the classpath.
Source
Thrown at netty/src/main/java/io/grpc/netty/Utils.java:482
* Returns TCP_USER_TIMEOUT channel option for Epoll channel if Epoll is available, otherwise
* null.
*/
@Nullable
static ChannelOption<Integer> maybeGetTcpUserTimeoutOption() {
return getEpollChannelOption("TCP_USER_TIMEOUT");
}
@Nullable
@SuppressWarnings("unchecked")
private static <T> ChannelOption<T> getEpollChannelOption(String optionName) {
if (isEpollAvailable()) {
try {
return
(ChannelOption<T>) Class.forName("io.netty.channel.epoll.EpollChannelOption")
.getField(optionName)
.get(null);
} catch (Exception e) {
throw new RuntimeException("ChannelOption(" + optionName + ") is not available", e);
}
}
return null;
}
private static final class DefaultEventLoopGroupResource implements Resource<EventLoopGroup> {
private final String name;
private final int numEventLoops;
private final EventLoopGroupType eventLoopGroupType;
DefaultEventLoopGroupResource(
int numEventLoops, String name, EventLoopGroupType eventLoopGroupType) {
this.name = name;
// See the implementation of MultithreadEventLoopGroup. DEFAULT_EVENT_LOOP_THREADS there
// defaults to NettyRuntime.availableProcessors() * 2. We don't think we need that many
// threads. The overhead of a thread includes file descriptors and at least one chunk
// allocation from PooledByteBufAllocator. Here we reduce the default number of threads by
// half.View on GitHub (pinned to 64daddc1f3)
Solutions
- Upgrade Netty (netty-transport-native-epoll) to a version that defines the requested EpollChannelOption field (e.g. TCP_USER_TIMEOUT, Netty 4.1.x recent)
- Align grpc-netty's transitive Netty version explicitly with your Netty dependencies to avoid shading/mixing versions
- Avoid enabling the option that requires the lookup (e.g. don't set tcp user timeout) if your Netty version lacks it
- Use NIO channel types so the epoll option lookup path is skipped
Example fix
// before (pom.xml) <dependency><groupId>io.netty</groupId><artifactId>netty-transport-native-epoll</artifactId><version>4.1.30.Final</version></dependency> // after <dependency><groupId>io.netty</groupId><artifactId>netty-transport-native-epoll</artifactId><version>4.1.100.Final</version><classifier>linux-x86_64</classifier></dependency>
Defensive patterns
Strategy: validation
Validate before calling
boolean tcpUserTimeoutSupported;
try {
Class.forName("io.netty.channel.epoll.EpollChannelOption").getField("TCP_USER_TIMEOUT");
tcpUserTimeoutSupported = true;
} catch (ReflectiveOperationException e) { tcpUserTimeoutSupported = false; } Try / catch
try {
channel = builder.build();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("ChannelOption(")) {
// downgrade: drop the option / use NIO and retry
channel = builder.channelType(NioSocketChannel.class).build();
} else { throw e; }
} Prevention
- Pin a Netty version new enough for the epoll options grpc-netty expects
- Avoid mixing shaded/relocated Netty classes with grpc-netty
- Feature-detect channel options instead of assuming TCP_USER_TIMEOUT exists
- Keep grpc-netty and netty versions upgraded together
When it happens
Trigger: grpc-netty calls maybeGetTcpUserTimeoutOption (e.g. when enableCheckEffectiveTcpUserTimeout / user timeout config is used) on a Netty version where EpollChannelOption lacks the requested field, or field access is blocked; only happens on epoll path on Linux.
Common situations: Netty version too old to define TCP_USER_TIMEOUT in EpollChannelOption; shaded/relocated Netty classes so the reflection target isn't the expected class; security manager or module restrictions blocking getField().get(null).
Related errors
- Cannot load EpollSocketChannel
- EpollEventLoopGroup constructor not found
- Cannot create Epoll EventLoopGroup
- Exception while checking Epoll availability
- Cannot load EpollDomainSocketChannel
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/4fb4673794331ccc.
Report an issue: GitHub.