{"record":{"id":"bc94a0214c36b679","repo":"apache/pulsar","slug":"invalid-cluster-id-clusterid","errorCode":null,"errorMessage":"Invalid cluster id: ${clusterId}","messagePattern":"Invalid cluster id: (.+?)","errorType":"http","errorClass":"RestException","httpStatus":403,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":865,"sourceCode":"                .thenApply(policies -> policies.replication_clusters);\n    }\n\n    @SuppressWarnings(\"checkstyle:WhitespaceAfter\")\n    protected CompletableFuture<Void> internalSetNamespaceReplicationClusters(List<String> clusterIds,\n                                                                              boolean compareTopicPartitions) {\n        return validateNamespacePolicyOperationAsync(namespaceName, PolicyName.REPLICATION, PolicyOperation.WRITE)\n                .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())\n                .thenApply(__ -> {\n                    if (CollectionUtils.isEmpty(clusterIds)) {\n                        throw new RestException(Status.PRECONDITION_FAILED, \"ClusterIds should not be null or empty\");\n                    }\n                    return Sets.newHashSet(clusterIds);\n                }).thenCompose(replicationClusterSet -> clustersAsync()\n                        .thenCompose(clusters -> {\n                            List<CompletableFuture<Void>> futures =\n                                    replicationClusterSet.stream().map(clusterId -> {\n                                        if (!clusters.contains(clusterId)) {\n                                            throw new RestException(Status.FORBIDDEN,\n                                                    \"Invalid cluster id: \" + clusterId);\n                                        }\n                                        return validatePeerClusterConflictAsync(clusterId, replicationClusterSet)\n                                            .thenCompose(__ -> getNamespacePoliciesAsync(this.namespaceName)\n                                                .thenCompose(nsPolicies -> {\n                                                    if (!Policies.checkNewReplicationClusters(nsPolicies,\n                                                            replicationClusterSet)) {\n                                                        String msg = String.format(\"Cluster [%s] is not in the \"\n                                                                + \"list of allowed clusters list for namespace \"\n                                                                + \"[%s]\", clusterId, namespaceName.toString());\n                                                        log.info(msg);\n                                                        throw new RestException(Status.BAD_REQUEST, msg);\n                                                    }\n                                                    return validateClusterForTenantAsync(\n                                                            namespaceName.getTenant(), clusterId);\n                                                }));\n                                    }).collect(Collectors.toList());\n                            return FutureUtil.waitForAll(futures).thenApply(__ -> replicationClusterSet);","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L847-L883","documentation":"HTTP 403 (FORBIDDEN) returned by the set-replication-clusters API when one of the requested clusterIds is not a cluster registered in this Pulsar instance (clustersAsync() does not contain it). The API refuses to point a namespace at an unknown cluster.","triggerScenarios":"POST /namespaces/{tenant}/{namespace}/replication containing a cluster id that was never created, was deleted, or is misspelled; the check is `!clusters.contains(clusterId)` inside internalSetNamespaceReplicationClusters.","commonSituations":"Config drift between environments (cluster exists in staging, not in prod); typo like 'cluster-a-west' vs 'cluster-a'; replication config copied from another Pulsar installation; cluster removed by another admin while replication config still references it.","solutions":["List registered clusters with GET /admin/v2/clusters and use exactly those names.","Create the missing cluster via PUT /admin/v2/clusters/{clusterId} if it should exist.","Fix typos/case mismatches in the clusterIds list.","Remove stale cluster ids from your replication configuration templates."],"exampleFix":"// before\nsetReplicationClusters(ns, List.of(\"cluster-a\", \"cluster-b\")); // cluster-b not registered\n// after\nSet<String> known = admin.clusters().getClusters();\nList<String> valid = clusterIds.stream().filter(known::contains).toList();\nsetReplicationClusters(ns, valid);","handlingStrategy":"validation","validationCode":"Set<String> registered = new HashSet<>(admin.clusters().getClusters());\nList<String> invalid = clusterIds.stream().filter(c -> !registered.contains(c)).toList();\nif (!invalid.isEmpty()) throw new IllegalArgumentException(\"Unknown clusters: \" + invalid);","typeGuard":null,"tryCatchPattern":"try {\n    admin.namespaces().setNamespaceReplicationClusters(ns, clusterIds);\n} catch (PulsarAdminException.NotAllowedException e) {\n    log.error(\"Invalid cluster id in replication set: {}\", clusterIds);\n}","preventionTips":["Fetch GET /admin/v2/clusters and validate ids before submitting","Keep cluster names in a shared, versioned config rather than free text","Watch for cluster deletions and update referencing namespaces","Mind case sensitivity and typos in cluster ids"],"tags":["rest-api","admin","validation","replication","cluster"],"backgroundTag":"invalid-cluster-id","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"}