quarkusio/quarkus · error · ChannelException
already bound
Error message
already bound
What it means
Quarkus's virtual-transport (Unix domain socket-style in-memory channel support for Netty) tracks bound channels in VirtualChannelRegistry. register() throws ChannelException("already bound") when a channel is registered while it already has a previous local address, i.e. bind() is called on an already-bound channel — Netty channels may only be bound once.
Source
Thrown at extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/virtual/VirtualChannelRegistry.java:33
*/
package io.quarkus.netty.runtime.virtual;
import java.net.SocketAddress;
import java.util.concurrent.ConcurrentMap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
final class VirtualChannelRegistry {
private static final ConcurrentMap<VirtualAddress, Channel> boundChannels = PlatformDependent.newConcurrentHashMap();
static VirtualAddress register(
Channel channel, VirtualAddress oldLocalAddress, SocketAddress localAddress) {
if (oldLocalAddress != null) {
throw new ChannelException("already bound");
}
if (!(localAddress instanceof VirtualAddress)) {
throw new ChannelException("unsupported address type: " + StringUtil.simpleClassName(localAddress));
}
VirtualAddress addr = (VirtualAddress) localAddress;
if (VirtualAddress.ANY.equals(addr)) {
addr = new VirtualAddress(channel);
}
Channel boundChannel = boundChannels.putIfAbsent(addr, channel);
if (boundChannel != null) {
throw new ChannelException("address already in use by: " + boundChannel);
}
return addr;
}
static Channel get(SocketAddress localAddress) {View on GitHub (pinned to e1c734241f)
Solutions
- Create a new channel (new Bootstrap/connect again) instead of rebinding the existing one.
- Close the already-bound channel before binding again.
- Check channel.isActive()/localAddress() before calling bind and skip if already bound.
Example fix
// before
if (!channel.isOpen()) { channel.close(); }
channel.bind(localAddress); // throws if already bound
// after
if (channel.isActive() && channel.localAddress() != null) {
return; // already bound
}
channel.bind(localAddress); Defensive patterns
Strategy: type-guard
Validate before calling
if (channel instanceof Channel && ((Channel) channel).localAddress() != null) {
throw new IllegalStateException("channel already bound: " + channel);
} Type guard
static boolean isBindable(Channel ch) {
return ch.isOpen() && ch.localAddress() == null;
} Try / catch
try {
channel.bind(localAddress).sync();
} catch (ChannelException e) {
if ("already bound".equals(e.getMessage())) {
// reuse existing channel or recreate
} else { throw e; }
} Prevention
- Never call bind() twice on the same channel; recreate via Bootstrap instead
- Check channel.localAddress() before binding
- Close channels deterministically in restart/reload paths
When it happens
Trigger: Calling bind() twice on the same VirtualServerChannel / VirtualChannel without closing or rebinding; registering a channel whose oldLocalAddress is non-null again through the virtual transport.
Common situations: Restart logic that re-binds a channel without recreating it; retry loops around bootstrap.bind() reusing the same channel/Bootstrap improperly; framework code double-binding during shutdown/reconnect handling.
Related errors
- unsupported address type:
- address already in use by:
- No virtual channel available
- IOException(e)
- Could not find Jetty NPN/ALPN or Conscrypt as installed JDK
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/b5c7c97971a77f57.
Report an issue: GitHub.