copy/v86 · error · Error

pool of dynamic TCP port numbers exhausted, connection abort

Error message

pool of dynamic TCP port numbers exhausted, connection aborted

What it means

When opening an outbound TCP connection, the fake network stack allocates a dynamic source port by probing TCP_DYNAMIC_PORT_RANGE candidate ports for an unused ip:port:ip:port tuple. If all candidate tuples are already in use, no port can be allocated and the connection is aborted with this error.

Source

Thrown at src/browser/fake_network.js:1033

        (spec.ipv4.dest[2] << 8 | spec.ipv4.dest[3]) +
        IPV4_PROTO_TCP +
        total_length;
    view.setUint16(16, calc_inet_checksum(total_length, pseudo_header, view, out));
    return total_length;
}

export function fake_tcp_connect(dport, adapter)
{
    const vm_ip_str = adapter.vm_ip.join(".");
    const router_ip_str = adapter.router_ip.join(".");
    const sport_0 = (Math.random() * TCP_DYNAMIC_PORT_RANGE) | 0;
    let sport, tuple, sport_i = 0;
    do {
        sport = TCP_DYNAMIC_PORT_START + ((sport_0 + sport_i) % TCP_DYNAMIC_PORT_RANGE);
        tuple = `${vm_ip_str}:${dport}:${router_ip_str}:${sport}`;
    } while(++sport_i < TCP_DYNAMIC_PORT_RANGE && adapter.tcp_conn[tuple]);
    if(adapter.tcp_conn[tuple]) {
        throw new Error("pool of dynamic TCP port numbers exhausted, connection aborted");
    }

    let conn = new TCPConnection(adapter);

    conn.tuple = tuple;
    conn.hsrc = adapter.router_mac;
    conn.psrc = adapter.router_ip;
    conn.sport = sport;
    conn.hdest = adapter.vm_mac;
    conn.dport = dport;
    conn.pdest = adapter.vm_ip;
    adapter.tcp_conn[tuple] = conn;
    conn.connect();
    return conn;
}

export function fake_tcp_probe(dport, adapter) {
    return new Promise((res, rej) => {

View on GitHub (pinned to 180830d539)

Solutions

  1. Close TCP connections that are no longer needed so their dynamic ports return to the pool (check for leaked/never-closed sockets)
  2. Increase TCP_DYNAMIC_PORT_START/TCP_DYNAMIC_PORT_RANGE constants to enlarge the ephemeral port pool
  3. Reduce the number of concurrent outbound connections from the guest and serialize heavy network work
  4. Add monitoring/logging of adapter.tcp_conn size to detect leaks early

Example fix

// before
conn = openTcp(dport); // throws after pool exhaustion
// after
try {
    conn = openTcp(dport);
} catch (e) {
    if (String(e.message).includes("dynamic TCP port")) {
    closeIdleConnections();
    conn = openTcp(dport); // retry after freeing ports
    }
}
Defensive patterns

Strategy: retry

Validate before calling

function portPoolUsage(adapter) {
    return Object.keys(adapter.tcp_conn)
    .filter(t => { const s = t.split(":")[3];
        return s >= TCP_DYNAMIC_PORT_START && s < TCP_DYNAMIC_PORT_START + TCP_DYNAMIC_PORT_RANGE; }).length;
}
if (portPoolUsage(adapter) >= TCP_DYNAMIC_PORT_RANGE) freeIdleConnections(adapter);

Try / catch

try {
    conn = openOutboundTcp(adapter, dport);
} catch (e) {
    if (e.message.includes("dynamic TCP port")) {
    closeIdleTcpConnections(adapter);
    conn = openOutboundTcp(adapter, dport); // retry once
    } else throw e;
}

Prevention

When it happens

Trigger: Calling the outbound TCP connect path when all TCP_DYNAMIC_PORT_RANGE dynamic source ports for the VM IP are bound by existing connections in adapter.tcp_conn. Occurs after ~65535 (TCP_DYNAMIC_PORT_RANGE) simultaneously open connections, or if connections leak (never closed) until the pool wraps around.

Common situations: Long-running emulated sessions that open many sockets without closing them (leaked connections keep their tuples reserved); heavy parallel downloads from the emulated OS exhausting ephemeral ports; a regression that fails to delete tcp_conn entries on close.

Understand the failure class

Related errors


AI-assisted analysis of copy/v86@180830d539 (2026-08-31). Data as JSON: /api/errors/502d0d1a25580b8a. Report an issue: GitHub.