{"record":{"id":"6d6aae14336b4e57","repo":"apache/pulsar","slug":"the-topic-has-a-max-partition-index-of-d-the-num","errorCode":null,"errorMessage":"The topic has a max partition index of %d, the number of partitions must be at least %d","messagePattern":"The topic has a max partition index of (.+?), the number of partitions must be at least (.+?)","errorType":"http","errorClass":"org.apache.pulsar.broker.admin.RestException","httpStatus":409,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java","lineNumber":627,"sourceCode":"                                    || (topicExistsInfo.getTopicType().equals(TopicType.PARTITIONED)\n                                    && !createLocalTopicOnly)) {\n                                log.warn()\n                                        .attr(\"topic\", topicName)\n                                        .log(\"Failed to create already existing topic\");\n                                throw new RestException(Status.CONFLICT, \"This topic already exists\");\n                            }\n                        }\n                    } finally {\n                        topicExistsInfo.recycle();\n                    }\n                })\n                .thenCompose(__ -> getMaxPartitionIndex(topicName)\n                        .thenAccept(existingMaxPartitionIndex -> {\n                            // Case 1: Metadata loss — user tries to recreate the partitioned topic.\n                            // Case 2: Non-partitioned topic — user attempts to convert it to a partitioned topic.\n                            if (existingMaxPartitionIndex >= numPartitions) {\n                                int requiredMinPartitions = existingMaxPartitionIndex + 1;\n                                throw new RestException(Status.CONFLICT, String.format(\n                                        \"The topic has a max partition index of %d, the number of partitions must be \"\n                                                + \"at least %d\",\n                                        existingMaxPartitionIndex, requiredMinPartitions));\n                            }\n                        }))\n                .thenRun(() -> {\n                    for (int i = 0; i < numPartitions; i++) {\n                        pulsar().getBrokerService().getTopicEventsDispatcher()\n                                .notify(topicName.getPartition(i).toString(), TopicEvent.CREATE,\n                                        EventStage.BEFORE);\n                    }\n                })\n                .thenCompose(__ -> provisionPartitionedTopicPath(numPartitions, createLocalTopicOnly, properties))\n                .thenCompose(__ -> tryCreatePartitionsAsync(numPartitions))\n                .thenRun(() -> {\n                    if (!createLocalTopicOnly\n                            && pulsar().getConfig().isCreateTopicToRemoteClusterForReplication()) {\n                        internalCreatePartitionedTopicToReplicatedClustersInBackground(numPartitions);","sourceCodeStart":609,"sourceCodeEnd":645,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java#L609-L645","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before (fails: existing max partition index 5)\nadmin.topics().createPartitionedTopic(\"persistent://public/default/my-topic\", 3);\n// after\nadmin.topics().createPartitionedTopic(\"persistent://public/default/my-topic\", 6);\n// or: delete partitions + metadata first, then create with desired count","handlingStrategy":"validation","validationCode":"// Java admin client\nPartitionedTopicMetadata meta;\ntry { meta = admin.topics().getPartitionedMetadata(topic); meta.partitions > 0 }\ncatch (PulsarAdminException.NotFoundException e) { /* check raw partition topics */ }\n// or simply parse the 409 message: existingMaxPartitionIndex, requiredMin = idx + 1","typeGuard":null,"tryCatchPattern":"try {\n    admin.topics().createPartitionedTopic(topic, numPartitions);\n} catch (PulsarAdminException.ConflictException e) {\n    // 409: raise numPartitions to requiredMinPartitions or delete existing partitions first\n}","preventionTips":["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."],"tags":["pulsar","broker","rest-conflict","partitioned-topic"],"backgroundTag":"partitioned-topic-conflict","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}