quarkusio/quarkus · error · RuntimeException

No virtual channel available

Error message

No virtual channel available

What it means

VirtualClientConnection.connect looks up the remote address in VirtualChannelRegistry to find a bound server channel. If no virtual channel is registered under that remote address, a RuntimeException("No virtual channel available") is thrown — the virtual-transport equivalent of 'connection refused': nothing is listening at that address.

Source

Thrown at extensions/netty/runtime/src/main/java/io/quarkus/netty/runtime/virtual/VirtualClientConnection.java:117

    public static VirtualClientConnection connect(VirtualResponseHandler handler, VirtualAddress remoteAddress) {
        return connect(handler, remoteAddress, remoteAddress);
    }

    /**
     * Establish a virtual intra-JVM connection
     *
     * @param remoteAddress
     * @param clientAddress
     * @return
     */
    public static VirtualClientConnection connect(VirtualResponseHandler handler, VirtualAddress remoteAddress,
            SocketAddress clientAddress) {
        if (clientAddress == null)
            clientAddress = remoteAddress;

        Channel boundChannel = VirtualChannelRegistry.get(remoteAddress);
        if (boundChannel == null) {
            throw new RuntimeException("No virtual channel available");
        }
        if (!(boundChannel instanceof VirtualServerChannel)) {
            throw new RuntimeException("Should be virtual server channel: " + boundChannel.getClass().getName());
        }

        VirtualServerChannel serverChannel = (VirtualServerChannel) boundChannel;
        VirtualClientConnection conn = new VirtualClientConnection(clientAddress, handler);
        conn.peer = serverChannel.serve(conn);
        return conn;
    }
}

View on GitHub (pinned to e1c734241f)

Solutions

  1. Ensure the virtual server channel is bound and still active before the client connects (await bind().sync() and check registry).
  2. Use the exact VirtualAddress instance returned by the server's bind for the client's remote address.
  3. Close connections before shutting the server down; keep server lifetime scoped around client usage.
  4. Add retry/await logic (e.g. Awaitility) for the registry entry to appear in concurrent tests.

Example fix

// before
VirtualClientConnection.connect(channel, peer, remoteAddress); // server not bound yet -> throws
// after
serverChannel.bind(localAddr).syncUninterruptibly(); // guarantee bound first
await().atMost(2, SECONDS).until(() -> VirtualChannelRegistry.get(localAddr) != null);
VirtualClientConnection.connect(channel, peer, localAddr);
Defensive patterns

Strategy: retry

Validate before calling

if (VirtualChannelRegistry.get(remoteAddress) == null) {
    throw new IllegalStateException("no virtual server bound at " + remoteAddress + "; bind the server first");
}

Try / catch

try {
    VirtualClientConnection.connect(channel, peer, remoteAddress);
} catch (RuntimeException e) {
    if ("No virtual channel available".equals(e.getMessage())) {
        await().atMost(2, SECONDS).until(() -> VirtualChannelRegistry.get(remoteAddress) != null);
        VirtualClientConnection.connect(channel, peer, remoteAddress);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Connecting a virtual client to a remote VirtualAddress that no server channel has bound; connecting before the server finished binding; address typo/mismatch between the bound VirtualAddress and the connect target; server channel closed (unregistered) before the client connects.

Common situations: In-memory client/server test setups where the client connect races the server bind; tests restarted without rebinding; using raw SocketAddress values that don't match the registered VirtualAddress instance; teardown ordering shutting down the server first.

Related errors


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