apache/pulsar · error · RestException

Cluster already exists

Error message

Cluster already exists

What it means

HTTP 409 (CONFLICT) thrown by the admin REST API when a client attempts to create a cluster whose name already exists in cluster metadata. The handler checks cluster existence asynchronously via clusterResources().getClusterAsync and throws before calling createClusterAsync. Cluster creation is idempotency-guarded: one name maps to one ClusterData record.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java:184

                )
            )
        ) ClusterDataImpl clusterData) {
        validateBothSuperuserAndClusterOperation(cluster, ClusterOperation.CREATE_CLUSTER)
                .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())
                .thenCompose(__ -> {
                    NamedEntity.checkName(cluster);
                    if (clusterData == null) {
                        throw new RestException(Status.BAD_REQUEST, "cluster data is required");
                    }
                    try {
                        clusterData.checkPropertiesIfPresent();
                    } catch (IllegalArgumentException ex) {
                        throw new RestException(Status.BAD_REQUEST, ex.getMessage());
                    }
                    return clusterResources().getClusterAsync(cluster);
                }).thenCompose(clusterOpt -> {
                    if (clusterOpt.isPresent()) {
                        throw new RestException(Status.CONFLICT, "Cluster already exists");
                    }
                    return clusterResources().createClusterAsync(cluster, clusterData);
                }).thenAccept(__ -> {
                    log.info().attr("cluster", cluster).log("Created cluster");
                    asyncResponse.resume(Response.ok().build());
                }).exceptionally(ex -> {
                    log.error()
                            .attr("cluster", cluster)
                            .exception(ex)
                            .log("Failed to create cluster");
                    Throwable realCause = FutureUtil.unwrapCompletionException(ex);
                    if (realCause instanceof IllegalArgumentException) {
                        asyncResponse.resume(new RestException(PRECONDITION_FAILED,
                                "Cluster name is not valid"));
                        return null;
                    }
                    resumeAsyncResponseExceptionally(asyncResponse, ex);
                    return null;

View on GitHub (pinned to 820761864e)

Solutions

  1. Use PUT /admin/v3/clusters/{cluster} (update/upsert semantics) instead of POST if you want create-or-update behavior.
  2. Check existence first with GET /admin/v3/clusters/{cluster}; if it returns 200 and config matches, treat the create as done.
  3. Delete the stale cluster with DELETE /admin/v3/clusters/{cluster} if it is leftover garbage and empty, then recreate.
  4. Pick a different, unique cluster name if the existing record is legitimate.

Example fix

// before
admin.clusters().createCluster(clusterName, clusterData); // 409 if exists
// after
try {
    admin.clusters().createCluster(clusterName, clusterData);
} catch (PulsarAdminException.ConflictException e) {
    admin.clusters().updateCluster(clusterName, clusterData); // upsert
}
Defensive patterns

Strategy: validation

Validate before calling

boolean exists;
try {
    admin.clusters().getCluster(clusterName);
    exists = true;
} catch (PulsarAdminException.NotFoundException e) {
    exists = false;
}
if (exists) {
    admin.clusters().updateCluster(clusterName, clusterData); // upsert instead of create
} else {
    admin.clusters().createCluster(clusterName, clusterData);
}

Try / catch

try {
    admin.clusters().createCluster(clusterName, clusterData);
} catch (PulsarAdminException.ConflictException e) {
    // cluster already present: verify config then update
    admin.clusters().updateCluster(clusterName, clusterData);
}

Prevention

When it happens

Trigger: POST /admin/v3/clusters/{cluster} where the cluster name is already present in the metadata store; concurrent createCluster calls racing on the same name; retrying a previously successful create request.

Common situations: Re-running bootstrap/provisioning scripts without checking existing clusters; two ops tools provisioning the same cluster concurrently; Terraform/Ansible re-apply after a partial failure where the cluster record was already written.

Related errors


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