apache/pulsar · warning · RestException
ResourceGroup already exists
Error message
ResourceGroup already exists
What it means
Returned with HTTP 409 CONFLICT when creating a resource group fails because one with the same name already exists in the metadata store (MetadataStoreException.AlreadyExistsException). The handler logs 'Failed to create ResourceGroup - already exists' at WARN and maps it to Response.Status.CONFLICT with message 'ResourceGroup already exists'. It is a name-collision signal, not an infrastructure failure.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ResourceGroupsBase.java:112
}
protected void internalCreateResourceGroup(String rgName, ResourceGroup rgConfig) {
rgConfig.setPublishRateInMsgs(rgConfig.getPublishRateInMsgs() == null
? -1 : rgConfig.getPublishRateInMsgs());
rgConfig.setPublishRateInBytes(rgConfig.getPublishRateInBytes() == null
? -1 : rgConfig.getPublishRateInBytes());
rgConfig.setDispatchRateInMsgs(rgConfig.getDispatchRateInMsgs() == null
? -1 : rgConfig.getDispatchRateInMsgs());
rgConfig.setDispatchRateInBytes(rgConfig.getDispatchRateInBytes() == null
? -1 : rgConfig.getDispatchRateInBytes());
try {
resourceGroupResources().createResourceGroup(rgName, rgConfig);
log.info().attr("resourceGroup", rgName).log("Created ResourceGroup");
} catch (MetadataStoreException.AlreadyExistsException e) {
log.warn()
.attr("resourceGroup", rgName)
.log("Failed to create ResourceGroup - already exists");
throw new RestException(Response.Status.CONFLICT, "ResourceGroup already exists");
} catch (Exception e) {
log.error()
.attr("resourceGroup", rgName)
.exception(e)
.log("Failed to create ResourceGroup");
throw new RestException(e);
}
}
protected void internalCreateOrUpdateResourceGroup(String rgName, ResourceGroup rgConfig) {
try {
validateSuperUserAccess();
checkNotNull(rgConfig);
/*
* see if ResourceGroup exists and treat the request as a update if it does.
*/
boolean rgExists = false;
try {View on GitHub (pinned to 820761864e)
Solutions
- Use the create-or-update (upsert) path instead of the create-only call if overwriting is acceptable.
- Check existence first (GET the resource group) and skip or update instead of creating.
- Catch the 409 on the client and fall back to an update operation.
- If caused by a race, serialize provisioning of resource groups (one actor per name).
Example fix
// before
admin.resourcegroups().createResourceGroup(rgName, rg); // 409 if exists
// after
try {
admin.resourcegroups().createResourceGroup(rgName, rg);
} catch (PulsarAdminException e) {
if (e.getStatusCode() == 409) {
admin.resourcegroups().updateResourceGroup(rgName, rg); // idempotent upsert
} else {
throw e;
}
} Defensive patterns
Strategy: fallback
Validate before calling
// pre-check existence
boolean exists;
try { admin.resourcegroups().getResourceGroup(rgName); exists = true; }
catch (PulsarAdminException e) { exists = e.getStatusCode() != 404; } Type guard
null
Try / catch
try {
admin.resourcegroups().createResourceGroup(rgName, rg);
} catch (PulsarAdminException e) {
if (e.getStatusCode() == 409) admin.resourcegroups().updateResourceGroup(rgName, rg); // upsert fallback
else throw e;
} Prevention
- Prefer the upsert endpoint over create-only when idempotency is desired.
- Check existence before creating in provisioning scripts.
- Serialize creation of same-named groups across automation.
When it happens
Trigger: PUT/POST create of /admin/v3/resourcegroups/{rgName} when the metadata store already contains an entry for rgName — e.g. a race between two create requests, or a create issued for an existing group via the create-only path (internalCreateResourceGroup).
Common situations: Two admins or automation scripts creating the same group concurrently; re-running a bootstrap script without idempotency; using the create endpoint expecting upsert semantics.
Related errors
- Cluster already exists
- ${duplicateBrokers} already exists in ${domainName}
- Cannot delete non empty namespace
- %s is a non-partitioned topic. Instead of calling delete-par
- e
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/6016ce9ba8e09e1a.
Report an issue: GitHub.