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

  1. Close/unbind the existing channel that holds the address before binding again.
  2. Bind with VirtualAddress.ANY (or a unique VirtualAddress) to let the registry assign an address.
  3. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/dd54b5e0439bb747. Report an issue: GitHub.