apache/pulsar · error · org.apache.pulsar.broker.admin.RestException
This topic already exists
Error message
This topic already exists
What it means
Returned by internalCreatePartitionedTopic when the topic already exists as a non-partitioned topic, or as a partitioned topic while createLocalTopicOnly is false — i.e. the requested create would conflict with an existing topic of incompatible kind. Mapped to HTTP 409 Conflict. Pulsar refuses to silently convert between non-partitioned and partitioned topics.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:614
+ " in namespace");
throw new RestException(Status.PRECONDITION_FAILED,
"Exceed maximum number of topics in namespace.");
}
});
}
return CompletableFuture.completedFuture(null);
})
.thenCompose(__ -> checkTopicExistsAsync(topicName))
.thenAccept(topicExistsInfo -> {
try {
if (topicExistsInfo.isExists()) {
if (topicExistsInfo.getTopicType().equals(TopicType.NON_PARTITIONED)
|| (topicExistsInfo.getTopicType().equals(TopicType.PARTITIONED)
&& !createLocalTopicOnly)) {
log.warn()
.attr("topic", topicName)
.log("Failed to create already existing topic");
throw new RestException(Status.CONFLICT, "This topic already exists");
}
}
} finally {
topicExistsInfo.recycle();
}
})
.thenCompose(__ -> getMaxPartitionIndex(topicName)
.thenAccept(existingMaxPartitionIndex -> {
// Case 1: Metadata loss — user tries to recreate the partitioned topic.
// Case 2: Non-partitioned topic — user attempts to convert it to a partitioned topic.
if (existingMaxPartitionIndex >= numPartitions) {
int requiredMinPartitions = existingMaxPartitionIndex + 1;
throw new RestException(Status.CONFLICT, String.format(
"The topic has a max partition index of %d, the number of partitions must be "
+ "at least %d",
existingMaxPartitionIndex, requiredMinPartitions));
}
}))View on GitHub (pinned to 820761864e)
Solutions
- Check existence first with topics().getPartitionedTopicMetadata(...) and skip create if it already exists (catch NotFound).
- If it exists as non-partitioned but should be partitioned, migrate: drain and delete the non-partitioned topic, then create it partitioned (there is no in-place conversion).
- Use the admin API's allow-auto-create / createLocalTopicOnly semantics or the client's createIfMissing style call for idempotent provisioning.
- Treat 409 as success in idempotent provisioning scripts rather than an error.
Example fix
// before
admin.topics().createPartitionedTopic("my-tenant/my-ns/orders", 8); // 409 on redeploy
// after
try {
admin.topics().createPartitionedTopic("my-tenant/my-ns/orders", 8);
} catch (PulsarAdminException.ConflictException e) {
// topic already provisioned — ignore
} Defensive patterns
Strategy: try-catch
Validate before calling
// Java: idempotent create — check before creating
PartitionedTopicMetadata md;
try {
md = admin.topics().getPartitionedTopicMetadata(fqTopic);
} catch (PulsarAdminException.NotFoundException e) {
md = null;
}
if (md == null) { admin.topics().createPartitionedTopic(fqTopic, n); } Try / catch
try {
admin.topics().createPartitionedTopic(fqTopic, n);
} catch (PulsarAdminException e) {
if (e.getStatusCode() == 409) {
// already provisioned — treat as success in idempotent flows
} else {
throw e;
}
} Prevention
- Make topic provisioning idempotent: check-then-create or swallow 409.
- Never silently convert a non-partitioned topic to partitioned — migrate explicitly.
- Record provisioning state so retries don't re-create with different settings.
When it happens
Trigger: createPartitionedTopic on a name that already exists as a non-partitioned topic; re-running createPartitionedTopic with createLocalTopicOnly=false when the partitioned topic already exists (idempotent re-create attempts without the create-if-missing flag).
Common situations: Automation that re-creates topics on deploy without checking existence; a topic was previously created non-partitioned and now must be partitioned; retry storms after a partially-failed create that actually succeeded.
Related errors
- Topic is not partitioned topic
- Partitioned Topic Name should not contain '-partition-'
- Exceed maximum number of topics in namespace.
- ${loadManagerClass} does not support this operation
- domain() invoked from wrong resource
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/78b4d2e083ee5377.
Report an issue: GitHub.