apache/pulsar · error · IllegalArgumentException

bindAddresses: ip:port `${ipPort}` is bound by two schemes:

Error message

bindAddresses: ip:port `${ipPort}` is bound by two schemes: `${priorListener}:${priorScheme}` and `${listener}:${scheme}`; an ip:port can carry only one scheme

What it means

IllegalArgumentException thrown by BindAddressValidator.validateBindAddresses when the same ip:port is bound by two protocol schemes (e.g. pulsar:// and pulsar+ssl://). A TCP socket can only be bound by one scheme, so after deduplicating by address, any cross-scheme collision on a non-ephemeral port (port != 0) is rejected. Entries with port 0 (ephemeral) are skipped.

Source

Thrown at pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/BindAddressValidator.java:122

                        + addr.getAddress() + ": `" + existing.getListenerName() + "` and `"
                        + addr.getListenerName() + "`");
            }
        }

        // ip:port uniqueness across protocol schemes. A TCP socket can only be bound by one
        // listener+scheme combination, so two bindings that share host:port but differ in scheme
        // (e.g. pulsar://0.0.0.0:8080 and http://0.0.0.0:8080) cannot both be active. Port 0 is
        // skipped because it means "OS-assigned ephemeral port" — the kernel will hand out a unique
        // port to each socket, so two port-0 entries with the same IP cannot actually collide.
        Map<String, BindAddress> uniqueIpPort = new LinkedHashMap<>();
        for (BindAddress addr : uniqueBindAddresses.values()) {
            if (addr.getAddress().getPort() == 0) {
                continue;
            }
            String ipPort = MultipleListenerValidator.formatHostPort(addr.getAddress());
            BindAddress prior = uniqueIpPort.putIfAbsent(ipPort, addr);
            if (prior != null) {
                throw new IllegalArgumentException("bindAddresses: ip:port `" + ipPort
                        + "` is bound by two schemes: `" + prior.getListenerName() + ":"
                        + prior.getAddress().getScheme() + "` and `" + addr.getListenerName() + ":"
                        + addr.getAddress().getScheme() + "`; an ip:port can carry only one scheme");
            }
        }

        return new ArrayList<>(uniqueBindAddresses.values());
    }

    /**
     * Generates bind addresses based on legacy configuration properties. The synthesized bindings are
     * tagged with the {@code internalListenerName} so that {@link MultipleListenerValidator} and
     * downstream code can correlate them with the internal advertised listener.
     */
    private static List<BindAddress> migrateBindAddresses(ServiceConfiguration config, String internalListenerName) {
        List<BindAddress> addresses = new ArrayList<>(4);
        String bindAddress = config.getBindAddress();
        if (config.getBrokerServicePort().isPresent()) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Assign a distinct port to each scheme (e.g. TLS on 6651)
  2. Keep exactly one scheme per ip:port in bindAddresses
  3. Audit the final listener table (name, scheme, host, port) before restart
  4. Use port 0 only when ephemeral assignment is intended (those are exempt)

Example fix

// before
bindAddresses=plain:pulsar://10.0.0.1:6650,tls:pulsar+ssl://10.0.0.1:6650
// after
bindAddresses=plain:pulsar://10.0.0.1:6650,tls:pulsar+ssl://10.0.0.1:6651
Defensive patterns

Strategy: validation

Validate before calling

Map<String,String> schemeByIpPort = new HashMap<>();
for (String s : bindAddresses.split(",")) {
    URI u = URI.create(s.substring(s.indexOf(':') + 1));
    if (u.getPort() == 0) continue;
    String key = u.getHost() + ":" + u.getPort();
    String prev = schemeByIpPort.put(key, u.getScheme());
    if (prev != null && !prev.equals(u.getScheme())) {
        throw new IllegalArgumentException("ip:port " + key + " used by two schemes: " + prev + " and " + u.getScheme());
    }
}

Prevention

When it happens

Trigger: bindAddresses like plain:pulsar://10.0.0.1:6650,tls:pulsar+ssl://10.0.0.1:6650 sharing host and port; a TLS listener accidentally configured on the plaintext port.

Common situations: Copy-pasting the plaintext listener config for TLS and updating only the scheme, not the port; k8s Services or sidecars mapping both schemes onto one port; quick config edits where the port digit was not incremented.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/df680543b447a2c2. Report an issue: GitHub.