grpc/grpc-java · critical · IOException
Failed to bind to address
Error message
Failed to bind to address ${address} What it means
After the batch bind completes, NettyServer.start() inspects each individual bind future. If a particular address's future failed, all channels are closed and an IOException "Failed to bind to address <address>" is thrown with that future's cause. This pinpoints which single address failed when multiple addresses were requested.
Solutions
- Read the cause from the IOException (typically BindException) to see why that address failed.
- Free the conflicting port or pick a different one; verify each address exists on the host.
- Use wildcard binding (0.0.0.0 or a port of 0 to get an ephemeral port) where feasible.
- Check interface availability inside containers/VMs before binding to a specific IP.
Example fix
// before
serverBuilder.addListenAddress(new InetSocketAddress("10.0.0.99", 50051)); // IP not assigned to host
// after
serverBuilder.addListenAddress(new InetSocketAddress("0.0.0.0", 50051));
Server server = serverBuilder.build();
try {
server.start();
} catch (IOException e) {
// e.getCause() explains which address failed and why
} Defensive patterns
Strategy: try-catch
Validate before calling
for (SocketAddress a : addresses) {
if (a instanceof InetSocketAddress && ((InetSocketAddress) a).getAddress() != null
&& !((InetSocketAddress) a).getAddress().isAnyLocalAddress()) {
// verify the interface IP exists on this host before binding
}
} Try / catch
try {
server.start();
} catch (IOException e) {
// e.getMessage() names the failing address; e.getCause() has the bind reason
throw new RuntimeException("Server failed to start: " + e.getMessage(), e);
} Prevention
- Bind to 0.0.0.0 unless a specific interface is required
- Verify each listen address/IP exists on the host (ip addr)
- Use unique ports per test server to avoid cross-test conflicts
When it happens
Trigger: Calling start() where one of the configured listen addresses fails to bind — per-address BindException (port in use), unreachable/unassigned interface address, or permission denied on a privileged port — while the overall bind call still returned futures to inspect.
Common situations: Multi-address servers where only one of several ports is occupied; binding to a specific local IP that is currently down; container environments where the requested interface doesn't exist; race where another process grabbed the port between check and bind.
Related errors
- Failed to bind to addresses
- All xds transports for authority are in backoff
- Can't set TLS settings for ALTS
- Cannot verify hostname
- ChannelLogger is not set in Builder
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/744a65ed523a34a1.
Report an issue: GitHub.
Appendix: source
Thrown at netty/src/main/java/io/grpc/netty/NettyServer.java:354
}
);
Map<ChannelFuture, SocketAddress> channelFutures =
bindCallFuture.awaitUninterruptibly().getNow();
if (!bindCallFuture.isSuccess()) {
channelGroup.close().awaitUninterruptibly();
throw new IOException(String.format("Failed to bind to addresses %s",
addresses), bindCallFuture.cause());
}
final List<InternalInstrumented<SocketStats>> socketStats = new ArrayList<>();
for (Map.Entry<ChannelFuture, SocketAddress> entry: channelFutures.entrySet()) {
// We'd love to observe interruption, but if interrupted we will need to close the channel,
// which itself would need an await() to guarantee the port is not used when the method
// returns. See #6850
final ChannelFuture future = entry.getKey();
if (!future.awaitUninterruptibly().isSuccess()) {
channelGroup.close().awaitUninterruptibly();
throw new IOException(String.format("Failed to bind to address %s",
entry.getValue()), future.cause());
}
final InternalInstrumented<SocketStats> listenSocketStats =
new ListenSocket(future.channel());
channelz.addListenSocket(listenSocketStats);
socketStats.add(listenSocketStats);
future.channel().closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
channelz.removeListenSocket(listenSocketStats);
}
});
}
listenSocketStatsList = Collections.unmodifiableList(socketStats);
}
@Override
public void shutdown() {View on GitHub (pinned to 64daddc1f3)