{"record":{"id":"34ca3b23f0eb2fc7","repo":"apache/pulsar","slug":"s-s-peer-clusters-s-can-t-be-part-of-replication","errorCode":null,"errorMessage":"%s's peer-clusters %s can't be part of replication-clusters %s","messagePattern":"(.+?)'s peer-clusters (.+?) can't be part of replication-clusters (.+?)","errorType":"http","errorClass":"RestException","httpStatus":409,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":429,"sourceCode":"     *\n     * @param clusterName given cluster whose peer-clusters can't be present into replication-cluster list\n     * @param replicationClusters replication-cluster list\n     * @deprecated use {@link #validatePeerClusterConflictAsync(String, Set)} instead\n     */\n    @Deprecated\n    protected void validatePeerClusterConflict(String clusterName, Set<String> replicationClusters) {\n        try {\n            ClusterData clusterData = clusterResources().getCluster(clusterName).orElseThrow(\n                    () -> new RestException(Status.PRECONDITION_FAILED, \"Invalid replication cluster \" + clusterName));\n            Set<String> peerClusters = clusterData.getPeerClusterNames();\n            if (peerClusters != null && !peerClusters.isEmpty()) {\n                Sets.SetView<String> conflictPeerClusters = Sets.intersection(peerClusters, replicationClusters);\n                if (!conflictPeerClusters.isEmpty()) {\n                    log.warn()\n                            .attr(\"cluster\", clusterName)\n                            .attr(\"conflictingPeerClusters\", conflictPeerClusters)\n                            .log(\"Peer clusters can't be part of replication clusters\");\n                    throw new RestException(Status.CONFLICT,\n                            String.format(\"%s's peer-clusters %s can't be part of replication-clusters %s\", clusterName,\n                                    conflictPeerClusters, replicationClusters));\n                }\n            }\n        } catch (RestException re) {\n            throw re;\n        } catch (Exception e) {\n            log.warn()\n                    .attr(\"cluster\", clusterName)\n                    .exception(e)\n                    .log(\"Failed to get cluster-data\");\n        }\n    }\n\n    protected CompletableFuture<Void> validatePeerClusterConflictAsync(String clusterName,\n                                                                       Set<String> replicationClusters) {\n        return clusterResources().getClusterAsync(clusterName)\n                .thenAccept(data -> {","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L411-L447","documentation":"Pulsar forbids a cluster's peer-clusters from also appearing in a namespace's replication-clusters, because replication to a peer would loop. The deprecated sync validatePeerClusterConflict computes the intersection of the cluster's peerClusterNames and the requested replicationClusters and throws 409 CONFLICT if any cluster appears in both sets.","triggerScenarios":"Setting/updating a namespace's replication clusters (namespaces API, setReplicationClusters) or cluster data while the target cluster's ClusterData.peerClusterNames contains one of the requested replication clusters. The offending cluster names are printed in the message.","commonSituations":"Configuring geo-replication between two clusters that were already declared peers of each other; leftover peerClusterNames from a previous replication topology; automated tooling copying the same set into both peer-clusters and replication-clusters.","solutions":["Remove the conflicting cluster names from the namespace's replication-clusters list","Or remove them from the cluster's peerClusterNames (pulsar-admin clusters update) if peer relationships are stale","Redesign topology: peer-clusters are for local service discovery among clusters sharing a namespace ownership; replication should use only non-peer clusters","Migrate callers to validatePeerClusterConflictAsync — this sync method is deprecated"],"exampleFix":"// before\npulsar-admin namespaces set-clusters my-tenant/ns --clusters c1,c2  // c2 is peer of c1's cluster\n// after: remove the peer conflict\npulsar-admin clusters update c1 --peer-cluster-names \n// then\npulsar-admin namespaces set-clusters my-tenant/ns --clusters c1,c2","handlingStrategy":"validation","validationCode":"// Reject replication clusters that overlap the cluster's peers, before calling the API\nClusterData cd = admin.clusters().getCluster(clusterName);\nSet<String> peers = cd.getPeerClusterNames() == null ? Set.of() : cd.getPeerClusterNames();\nSet<String> conflict = new HashSet<>(peers); conflict.retainAll(replicationClusters);\nif (!conflict.isEmpty()) throw new IllegalArgumentException(\"Conflicting peer clusters: \" + conflict);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat peer-clusters and replication-clusters as disjoint sets in your config tooling","Validate cluster topology in CI before applying admin API changes","Clean up stale peerClusterNames when decommissioning replication links"],"tags":["configuration","geo-replication","http-409","cluster"],"backgroundTag":"peer-cluster-replication-conflict","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}