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

  1. Create a new channel (new Bootstrap/connect again) instead of rebinding the existing one.
  2. Close the already-bound channel before binding again.
  3. 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

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


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