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

  1. Pass at least one valid cluster id in clusterIds.
  2. Fix the template/config that resolved to an empty list — set a default local cluster.
  3. To change replication, supply the new full list (e.g. [local]) rather than emptying it.
  4. 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

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


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