apache/pulsar · error · org.apache.pulsar.broker.admin.RestException
The topic has a max partition index of %d, the number of par
Error message
The topic has a max partition index of %d, the number of partitions must be at least %d
What it means
Pulsar rejects creating a partitioned topic when its existing max partition index is >= the requested number of partitions. This guards against metadata loss (the partitioned-topic metadata was deleted but partition topics still exist) and against converting an existing non-partitioned topic (whose name occupies partition index -1 style space) into a partitioned topic with too few partitions. It prevents silently reusing or shadowing existing partition data.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:627
|| (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));
}
}))
.thenRun(() -> {
for (int i = 0; i < numPartitions; i++) {
pulsar().getBrokerService().getTopicEventsDispatcher()
.notify(topicName.getPartition(i).toString(), TopicEvent.CREATE,
EventStage.BEFORE);
}
})
.thenCompose(__ -> provisionPartitionedTopicPath(numPartitions, createLocalTopicOnly, properties))
.thenCompose(__ -> tryCreatePartitionsAsync(numPartitions))
.thenRun(() -> {
if (!createLocalTopicOnly
&& pulsar().getConfig().isCreateTopicToRemoteClusterForReplication()) {
internalCreatePartitionedTopicToReplicatedClustersInBackground(numPartitions);View on GitHub (pinned to 820761864e)
Solutions
- Increase the requested partition count to at least existingMaxPartitionIndex + 1 (the message tells you both numbers), e.g. if max index is 5, request >= 6 partitions.
- Fully delete the old topic first: delete each individual partition (topic-partition-N) and the partitioned-topic metadata, then recreate with the desired partition count.
- If this is metadata loss (partitions exist but no partitioned metadata), clean up the orphaned partition topics via the admin API or use the partitions recovery/lookup tooling, then create the topic.
- If you merely want a non-partitioned topic to become partitioned, pick a different topic name or delete the non-partitioned topic first (after draining consumers).
Example fix
// before (fails: existing max partition index 5)
admin.topics().createPartitionedTopic("persistent://public/default/my-topic", 3);
// after
admin.topics().createPartitionedTopic("persistent://public/default/my-topic", 6);
// or: delete partitions + metadata first, then create with desired count Defensive patterns
Strategy: validation
Validate before calling
// Java admin client
PartitionedTopicMetadata meta;
try { meta = admin.topics().getPartitionedMetadata(topic); meta.partitions > 0 }
catch (PulsarAdminException.NotFoundException e) { /* check raw partition topics */ }
// or simply parse the 409 message: existingMaxPartitionIndex, requiredMin = idx + 1 Try / catch
try {
admin.topics().createPartitionedTopic(topic, numPartitions);
} catch (PulsarAdminException.ConflictException e) {
// 409: raise numPartitions to requiredMinPartitions or delete existing partitions first
} Prevention
- Look up existing partitioned metadata before creating; skip or adapt if it already exists.
- Make partition count a stable config value per topic, not recomputed per run.
- When deleting partitioned topics, delete all partitions and metadata atomically in scripts.
- On metadata-loss recovery, use the topic-partition listing to determine the true max index before recreating.
When it happens
Trigger: Calling the admin createPartitionedTopic API (POST /admin/v2/persistent/{tenant}/{namespace}/{topic}/partitions with body N) when a topic with the same name already exists whose highest partition index is >= N — e.g. recreating a previously deleted partitioned topic, or calling createPartitionedTopic on a topic name that is already used by a non-partitioned topic.
Common situations: 1) Namespace bundle was removed or ZooKeeper metadata was lost while partition topics remained in the managed ledger — 'metadata loss'. 2) User created a plain topic 'my-topic' earlier, then tries createPartitionedTopic('my-topic', 3). 3) Automation/scripts that recreate topics after cleanup with fewer partitions than before.
Related errors
- Another partition exists for [${topicName}].
- entryFilterNames can't be empty. To remove entry filters use
- The offloadPolicies must be specified for namespace offload.
- The driver is not supported, support value: ${supportedDrive
- The bucket must be specified for namespace offload.
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/6d6aae14336b4e57.
Report an issue: GitHub.