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
- Assign a distinct port to each scheme (e.g. TLS on 6651)
- Keep exactly one scheme per ip:port in bindAddresses
- Audit the final listener table (name, scheme, host, port) before restart
- 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
- Give each scheme its own port (6650 plaintext, 6651 TLS)
- Never change only the scheme when duplicating a listener entry
- Skip port 0 entries when planning (they are exempt)
- Review the final bind table on every TLS rollout
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
- bindAddresses: conflicting listener names for ${address}: `$
- No ${scheme} URL configured for broker ${brokerId}
- Protocol handler for `${handler}` attempts to use ${address}
- Issuer URL does not use https, but must:
- Failed to parse tlsFactoryConfig as a JSON object
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/df680543b447a2c2.
Report an issue: GitHub.