quarkusio/quarkus · error · ChannelException
address already in use by:
Error message
address already in use by:
What it means
When binding a virtual channel, the registry stores the address→channel mapping atomically with putIfAbsent. If another channel is already bound to that VirtualAddress, the bind fails with ChannelException("address already in use by: <channel>") — the virtual equivalent of the OS 'address already in use' error.
Source
Thrown at extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/virtual/VirtualChannelRegistry.java:46
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) {
return boundChannels.get(localAddress);
}
static void unregister(VirtualAddress localAddress) {
boundChannels.remove(localAddress);
}
private VirtualChannelRegistry() {
// Unused
}
}
View on GitHub (pinned to e1c734241f)
Solutions
- Close/unbind the existing channel that holds the address before binding again.
- Bind with VirtualAddress.ANY (or a unique VirtualAddress) to let the registry assign an address.
- Track and reuse the original bound channel instead of creating a new one on restart.
Example fix
// before
Channel old = VirtualChannelRegistry.get(addr);
// assume free, but it is not:
serverChannel.bind(addr); // ChannelException
// after
Channel existing = VirtualChannelRegistry.get(addr);
if (existing != null) { existing.close().syncUninterruptibly(); }
serverChannel.bind(addr); Defensive patterns
Strategy: try-catch
Validate before calling
Channel existing = VirtualChannelRegistry.get(addr);
if (existing != null && existing.isActive()) {
throw new IllegalStateException("address " + addr + " already bound by " + existing);
} Try / catch
try {
channel.bind(addr).sync();
} catch (ChannelException e) {
if (e.getMessage().startsWith("address already in use")) {
Channel holder = VirtualChannelRegistry.get(addr);
if (holder != null) holder.close().syncUninterruptibly();
channel.bind(addr).sync();
} else { throw e; }
} Prevention
- Close old server channels before rebinding the same virtual address
- Bind with VirtualAddress.ANY for unique per-channel addresses in tests
- Ensure test teardown closes all virtual channels to free registry entries
When it happens
Trigger: Two channels trying to bind the same VirtualAddress concurrently or sequentially without the first being unbound; re-binding a server on an address whose previous channel was never closed/unregistered.
Common situations: Dev-mode live reload where the old virtual server channel still holds the address; parallel tests each trying to bind the same in-memory address; leaked channels from earlier failed runs keeping registry entries alive.
Related errors
- already bound
- unsupported address type:
- 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/dd54b5e0439bb747.
Report an issue: GitHub.