{"record":{"id":"df680543b447a2c2","repo":"apache/pulsar","slug":"bindaddresses-ip-port-ipport-is-bound-by-two","errorCode":null,"errorMessage":"bindAddresses: ip:port `${ipPort}` is bound by two schemes: `${priorListener}:${priorScheme}` and `${listener}:${scheme}`; an ip:port can carry only one scheme","messagePattern":"bindAddresses: ip:port `(.+?)` is bound by two schemes: `(.+?):(.+?)` and `(.+?):(.+?)`; an ip:port can carry only one scheme","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/BindAddressValidator.java","lineNumber":122,"sourceCode":"                        + addr.getAddress() + \": `\" + existing.getListenerName() + \"` and `\"\n                        + addr.getListenerName() + \"`\");\n            }\n        }\n\n        // ip:port uniqueness across protocol schemes. A TCP socket can only be bound by one\n        // listener+scheme combination, so two bindings that share host:port but differ in scheme\n        // (e.g. pulsar://0.0.0.0:8080 and http://0.0.0.0:8080) cannot both be active. Port 0 is\n        // skipped because it means \"OS-assigned ephemeral port\" — the kernel will hand out a unique\n        // port to each socket, so two port-0 entries with the same IP cannot actually collide.\n        Map<String, BindAddress> uniqueIpPort = new LinkedHashMap<>();\n        for (BindAddress addr : uniqueBindAddresses.values()) {\n            if (addr.getAddress().getPort() == 0) {\n                continue;\n            }\n            String ipPort = MultipleListenerValidator.formatHostPort(addr.getAddress());\n            BindAddress prior = uniqueIpPort.putIfAbsent(ipPort, addr);\n            if (prior != null) {\n                throw new IllegalArgumentException(\"bindAddresses: ip:port `\" + ipPort\n                        + \"` is bound by two schemes: `\" + prior.getListenerName() + \":\"\n                        + prior.getAddress().getScheme() + \"` and `\" + addr.getListenerName() + \":\"\n                        + addr.getAddress().getScheme() + \"`; an ip:port can carry only one scheme\");\n            }\n        }\n\n        return new ArrayList<>(uniqueBindAddresses.values());\n    }\n\n    /**\n     * Generates bind addresses based on legacy configuration properties. The synthesized bindings are\n     * tagged with the {@code internalListenerName} so that {@link MultipleListenerValidator} and\n     * downstream code can correlate them with the internal advertised listener.\n     */\n    private static List<BindAddress> migrateBindAddresses(ServiceConfiguration config, String internalListenerName) {\n        List<BindAddress> addresses = new ArrayList<>(4);\n        String bindAddress = config.getBindAddress();\n        if (config.getBrokerServicePort().isPresent()) {","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/validator/BindAddressValidator.java#L104-L140","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)"],"exampleFix":"// before\nbindAddresses=plain:pulsar://10.0.0.1:6650,tls:pulsar+ssl://10.0.0.1:6650\n// after\nbindAddresses=plain:pulsar://10.0.0.1:6650,tls:pulsar+ssl://10.0.0.1:6651","handlingStrategy":"validation","validationCode":"Map<String,String> schemeByIpPort = new HashMap<>();\nfor (String s : bindAddresses.split(\",\")) {\n    URI u = URI.create(s.substring(s.indexOf(':') + 1));\n    if (u.getPort() == 0) continue;\n    String key = u.getHost() + \":\" + u.getPort();\n    String prev = schemeByIpPort.put(key, u.getScheme());\n    if (prev != null && !prev.equals(u.getScheme())) {\n        throw new IllegalArgumentException(\"ip:port \" + key + \" used by two schemes: \" + prev + \" and \" + u.getScheme());\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["configuration","bind-address","tls","port-conflict"],"backgroundTag":"address-already-in-use","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}