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

  1. Read the cause from the IOException (typically BindException) to see why that address failed.
  2. Free the conflicting port or pick a different one; verify each address exists on the host.
  3. Use wildcard binding (0.0.0.0 or a port of 0 to get an ephemeral port) where feasible.
  4. 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

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


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)