apache/pulsar · error · RestException

Invalid cluster id: ${clusterId}

Error message

Invalid cluster id: ${clusterId}

What it means

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.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java:865

                .thenApply(policies -> policies.replication_clusters);
    }

    @SuppressWarnings("checkstyle:WhitespaceAfter")
    protected CompletableFuture<Void> internalSetNamespaceReplicationClusters(List<String> clusterIds,
                                                                              boolean compareTopicPartitions) {
        return validateNamespacePolicyOperationAsync(namespaceName, PolicyName.REPLICATION, PolicyOperation.WRITE)
                .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())
                .thenApply(__ -> {
                    if (CollectionUtils.isEmpty(clusterIds)) {
                        throw new RestException(Status.PRECONDITION_FAILED, "ClusterIds should not be null or empty");
                    }
                    return Sets.newHashSet(clusterIds);
                }).thenCompose(replicationClusterSet -> clustersAsync()
                        .thenCompose(clusters -> {
                            List<CompletableFuture<Void>> futures =
                                    replicationClusterSet.stream().map(clusterId -> {
                                        if (!clusters.contains(clusterId)) {
                                            throw new RestException(Status.FORBIDDEN,
                                                    "Invalid cluster id: " + clusterId);
                                        }
                                        return validatePeerClusterConflictAsync(clusterId, replicationClusterSet)
                                            .thenCompose(__ -> getNamespacePoliciesAsync(this.namespaceName)
                                                .thenCompose(nsPolicies -> {
                                                    if (!Policies.checkNewReplicationClusters(nsPolicies,
                                                            replicationClusterSet)) {
                                                        String msg = String.format("Cluster [%s] is not in the "
                                                                + "list of allowed clusters list for namespace "
                                                                + "[%s]", clusterId, namespaceName.toString());
                                                        log.info(msg);
                                                        throw new RestException(Status.BAD_REQUEST, msg);
                                                    }
                                                    return validateClusterForTenantAsync(
                                                            namespaceName.getTenant(), clusterId);
                                                }));
                                    }).collect(Collectors.toList());
                            return FutureUtil.waitForAll(futures).thenApply(__ -> replicationClusterSet);

View on GitHub (pinned to 820761864e)

Solutions

  1. List registered clusters with GET /admin/v2/clusters and use exactly those names.
  2. Create the missing cluster via PUT /admin/v2/clusters/{clusterId} if it should exist.
  3. Fix typos/case mismatches in the clusterIds list.
  4. Remove stale cluster ids from your replication configuration templates.

Example fix

// before
setReplicationClusters(ns, List.of("cluster-a", "cluster-b")); // cluster-b not registered
// after
Set<String> known = admin.clusters().getClusters();
List<String> valid = clusterIds.stream().filter(known::contains).toList();
setReplicationClusters(ns, valid);
Defensive patterns

Strategy: validation

Validate before calling

Set<String> registered = new HashSet<>(admin.clusters().getClusters());
List<String> invalid = clusterIds.stream().filter(c -> !registered.contains(c)).toList();
if (!invalid.isEmpty()) throw new IllegalArgumentException("Unknown clusters: " + invalid);

Try / catch

try {
    admin.namespaces().setNamespaceReplicationClusters(ns, clusterIds);
} catch (PulsarAdminException.NotAllowedException e) {
    log.error("Invalid cluster id in replication set: {}", clusterIds);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/bc94a0214c36b679. Report an issue: GitHub.