{"record":{"id":"163a2cebb24f24db","repo":"apache/pulsar","slug":"partitioned-topic-name-should-not-contain-partit-163a2c","errorCode":null,"errorMessage":"Partitioned Topic Name should not contain '-partition-'","messagePattern":"Partitioned Topic Name should not contain '-partition-'","errorType":"http","errorClass":"RestException","httpStatus":412,"severity":"warning","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java","lineNumber":988,"sourceCode":"    })\n    public void updatePartitionedTopic(\n            @Suspended final AsyncResponse asyncResponse,\n            @Parameter(description = \"Specify the tenant\", required = true)\n            @PathParam(\"tenant\") String tenant,\n            @Parameter(description = \"Specify the namespace\", required = true)\n            @PathParam(\"namespace\") String namespace,\n            @Parameter(description = \"Specify topic name\", required = true)\n            @PathParam(\"topic\") @Encoded String encodedTopic,\n            @QueryParam(\"updateLocalTopicOnly\") @DefaultValue(\"false\") boolean updateLocalTopic,\n            @Parameter(description = \"Whether leader broker redirected this call to this broker. For internal use.\")\n            @QueryParam(\"authoritative\") @DefaultValue(\"false\") boolean authoritative,\n            @QueryParam(\"force\") @DefaultValue(\"false\") boolean force,\n            @RequestBody(description = \"The number of partitions for the topic\",\n                    required = true, content = @Content(schema = @Schema(type = \"integer\", defaultValue = \"0\")))\n                    int numPartitions) {\n        validateTopicName(tenant, namespace, encodedTopic);\n        if (topicName.isPartitioned()) {\n            throw new RestException(Response.Status.PRECONDITION_FAILED,\n                    \"Partitioned Topic Name should not contain '-partition-'\");\n        }\n        validateTopicPolicyOperationAsync(topicName, PolicyName.PARTITION, PolicyOperation.WRITE)\n                .thenCompose(__ -> internalUpdatePartitionedTopicAsync(numPartitions, updateLocalTopic, force))\n                .thenAccept(__ -> {\n                    log.info()\n                            .attr(\"topic\", topicName)\n                            .attr(\"numPartitions\", numPartitions)\n                            .log(\"Updated topic partitions\");\n                    asyncResponse.resume(Response.noContent().build());\n                })\n                .exceptionally(ex -> {\n                    if (isNot307And404Exception(ex) && !isConflictException(ex)) {\n                        log.error()\n                                .attr(\"topic\", topicName)\n                                .attr(\"numPartitions\", numPartitions)\n                                .exception(ex)\n                                .log(\"Failed to update partitions\");","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/PersistentTopics.java#L970-L1006","documentation":"The updatePartitionedTopic (create/update partitions) admin endpoint rejects a topicName that already includes '-partition-N', because partition counts can only be managed on the base partitioned topic. It returns HTTP 412 PRECONDITION_FAILED.","triggerScenarios":"PUT/POST to /admin/v2/persistent/{tenant}/{namespace}/{topic}/partitions where topic contains '-partition-N' (e.g. my-topic-partition-0).","commonSituations":"Automation that captured a concrete partition name from a client URL or stats and passed it to the partition-management API.","solutions":["Call the endpoint with the base topic name without the '-partition-N' suffix","Strip the partition suffix before constructing the admin URL","List namespace topics and use the non-partition-suffixed name as the partitioned topic handle"],"exampleFix":"// before\nadmin.topics().updatePartitionedTopic(\"persistent://public/default/my-topic-partition-0\", 5);\n// after\nadmin.topics().updatePartitionedTopic(\"persistent://public/default/my-topic\", 5);","handlingStrategy":"validation","validationCode":"if (topic.matches(\".*-partition-\\\\d+$\")) throw new IllegalArgumentException(\"Pass base topic without -partition-N: \" + topic);","typeGuard":"boolean isPartitionHandle(String t) { return t != null && t.matches(\".*-partition-\\\\d+$\"); }","tryCatchPattern":"try { admin.topics().updatePartitionedTopic(base, n); } catch (PulsarAdminException e) { if (e.getStatusCode() == 412) { /* use base topic */ } }","preventionTips":["Never pass concrete partition names to partition-management endpoints","Normalize names via TopicName and use getPartitionedTopicName()","Document that partitions are managed only via the parent topic"],"tags":["rest-api","pulsar-admin","topic-partitions"],"backgroundTag":"partitioned-topic-name-rejected","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"}