{"record":{"id":"1e677f89b47a2736","repo":"apache/pulsar","slug":"duplicatebrokers-already-exists-in-domainname","errorCode":null,"errorMessage":"${duplicateBrokers} already exists in ${domainName}","messagePattern":"(.+?) already exists in (.+?)","errorType":"http","errorClass":"RestException","httpStatus":409,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java","lineNumber":1202,"sourceCode":"        if (inputDomain == null || inputDomain.brokers == null) {\n            return CompletableFuture.completedFuture(null);\n        }\n        return clusterResources().getFailureDomainResources()\n                .listFailureDomainsAsync(cluster)\n                .thenCompose(domainNames -> {\n                    List<CompletableFuture<Void>> futures = domainNames.stream()\n                            .filter(domainName -> !domainName.equals(inputDomainName))\n                            .map(domainName -> clusterResources()\n                                    .getFailureDomainResources().getFailureDomainAsync(cluster, domainName)\n                                    .thenAccept(failureDomainOpt -> {\n                                        if (failureDomainOpt.isPresent()\n                                                && CollectionUtils.isNotEmpty(failureDomainOpt.get().getBrokers())) {\n                                            List<String> duplicateBrokers = failureDomainOpt.get()\n                                                    .getBrokers().stream().parallel()\n                                                    .filter(inputDomain.brokers::contains)\n                                                    .collect(Collectors.toList());\n                                            if (CollectionUtils.isNotEmpty(duplicateBrokers)) {\n                                                throw new RestException(Status.CONFLICT,\n                                                        duplicateBrokers + \" already exists in \" + domainName);\n                                            }\n                                        }\n                                    }).exceptionally(ex -> {\n                                        Throwable realCause = FutureUtil.unwrapCompletionException(ex);\n                                        if (realCause instanceof WebApplicationException) {\n                                            throw FutureUtil.wrapToCompletionException(ex);\n                                        }\n                                        if (realCause instanceof NotFoundException) {\n                                                log.debug()\n                                                        .exception(ex)\n                                                        .log(\"Domain is not configured for cluster\");\n                                                                                        return null;\n                                        }\n                                        log.warn().attr(\"domain\", domainName).exception(ex).log(\"Failed to get domain\");\n                                        return null;\n                                    })\n                            ).collect(Collectors.toList());","sourceCodeStart":1184,"sourceCodeEnd":1220,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java#L1184-L1220","documentation":"HTTP 409 (CONFLICT) thrown by setFailureDomain when one or more brokers requested for a new/updated failure domain are already members of another failure domain in the same cluster. The handler loads the other domain (domainName), intersects its broker list with the requested brokers, and reports the duplicates in the message. A broker may belong to only one failure domain per cluster.","triggerScenarios":"POST /admin/v3/clusters/{cluster}/failureDomains/{domainName} whose brokers list overlaps brokers already assigned to a different domain; moving brokers between domains without first removing them from the old domain; partial prior update that left brokers registered elsewhere.","commonSituations":"Reorganization of failure domains during hardware rebalancing; scripts re-adding brokers without deduplicating against existing domains; recovery after an aborted domain reconfiguration.","solutions":["Remove the listed brokers from their current failure domain (POST the old domain without those brokers) before assigning them.","Update the target domain's broker list to exclude brokers that already belong to another domain, then move them one step at a time.","Inspect existing domains via GET /admin/v3/clusters/{cluster}/failureDomains and each domain's brokers to plan a non-overlapping assignment.","If the old domain should no longer exist, delete it (DELETE .../failureDomains/{oldDomain}) then re-submit the new domain."],"exampleFix":"// before\nFailureDomainData d = FailureDomainData.builder()\n        .brokers(List.of(\"b1:8080\", \"b2:8080\")).build(); // b1 already in domainA -> 409\nadmin.clusters().setFailureDomain(\"c1\", \"domainB\", d);\n// after\nFailureDomainData old = admin.clusters().getFailureDomain(\"c1\", \"domainA\");\nadmin.clusters().setFailureDomain(\"c1\", \"domainA\", FailureDomainData.builder()\n        .brokers(old.getBrokers().stream().filter(b -> !b.equals(\"b1:8080\")).collect(Collectors.toList())).build());\nadmin.clusters().setFailureDomain(\"c1\", \"domainB\", d);","handlingStrategy":"validation","validationCode":"// build the set of brokers already assigned to other domains\nSet<String> assigned = new HashSet<>();\nfor (String domain : admin.clusters().getFailureDomains(cluster)) {\n    if (!domain.equals(targetDomain)) {\n        assigned.addAll(admin.clusters().getFailureDomain(cluster, domain).getBrokers());\n    }\n}\nList<String> dupes = requestedBrokers.stream().filter(assigned::contains).collect(Collectors.toList());\nif (!dupes.isEmpty()) {\n    throw new IllegalArgumentException(\"Brokers already in another domain: \" + dupes);\n}\nadmin.clusters().setFailureDomain(cluster, targetDomain,\n        FailureDomainData.builder().brokers(requestedBrokers).build());","typeGuard":null,"tryCatchPattern":"try {\n    admin.clusters().setFailureDomain(cluster, domainName, domainData);\n} catch (PulsarAdminException.ConflictException e) {\n    // message lists duplicate brokers: detach them from their current domain first\n}","preventionTips":["Compute existing domain membership before writing a domain's broker list.","Move brokers in two steps: remove from old domain, then add to new domain.","Keep a single source of truth for broker-to-domain mapping in config management."],"tags":["rest-api","conflict","failure-domain","broker-assignment"],"backgroundTag":"duplicate-assignment-conflict","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"}