apache/pulsar · error · RestException
ClusterIds should not be null or empty
Error message
ClusterIds should not be null or empty
What it means
HTTP 412 (PRECONDITION_FAILED) returned by the set-replication-clusters admin API when the clusterIds list is null or empty. A namespace must always replicate to at least one cluster, so clearing the list entirely is rejected before any policy write occurs.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java:857
}
/**
* Directly get the replication clusters for a namespace, without checking allowed clusters.
*/
protected CompletableFuture<Set<String>> internalGetNamespaceReplicationClustersAsync() {
return validateNamespacePolicyOperationAsync(namespaceName, PolicyName.REPLICATION, PolicyOperation.READ)
.thenCompose(__ -> getNamespacePoliciesAsync(namespaceName))
.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());View on GitHub (pinned to 820761864e)
Solutions
- Pass at least one valid cluster id in clusterIds.
- Fix the template/config that resolved to an empty list — set a default local cluster.
- To change replication, supply the new full list (e.g. [local]) rather than emptying it.
- Add client-side preflight: reject empty clusterIds before calling the API.
Example fix
// before
setReplicationClusters(ns, clusters); // clusters = []
// after
if (clusters == null || clusters.isEmpty()) {
clusters = List.of(localClusterId); // never empty
}
setReplicationClusters(ns, clusters); Defensive patterns
Strategy: validation
Validate before calling
if (clusterIds == null || clusterIds.isEmpty()) {
throw new IllegalArgumentException("clusterIds must contain at least one cluster");
} Try / catch
try {
admin.namespaces().setNamespaceReplicationClusters(ns, clusterIds);
} catch (PulsarAdminException.PreconditionFailedException e) {
log.error("Refusing empty replication cluster set for {}", ns);
} Prevention
- Always include at least the local cluster in replication config
- Add non-empty assertions in automation before calling the API
- Fix templates that render empty cluster lists from unset config values
- Never use an empty list to 'disable' replication — set a fallback cluster instead
When it happens
Trigger: POST /namespaces/{tenant}/{namespace}/replication with a null body field or an empty JSON array for clusterIds, via internalSetNamespaceReplicationClusters.
Common situations: Automation templating that renders an empty cluster list (unset config value); code that computes clusters dynamically and produces []; attempting to 'disable' replication by sending an empty list.
Related errors
- Invalid cluster id: ${clusterId}
- Peer cluster ${peerCluster} does not exist
- Cluster [${clusterId}] is not in the list of allowed cluster
- cluster data is required
- Cluster ${cluster} does not exist.
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/c001b8dedd63e918.
Report an issue: GitHub.