{"record":{"id":"3515120c795b6a8a","repo":"apache/pulsar","slug":"partitioned-topic-name-should-not-contain-partit","errorCode":null,"errorMessage":"Partitioned Topic Name should not contain '-partition-'","messagePattern":"Partitioned Topic Name should not contain '-partition-'","errorType":"http","errorClass":"org.apache.pulsar.broker.admin.RestException","httpStatus":412,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java","lineNumber":286,"sourceCode":"        } catch (IllegalArgumentException e) {\n            log.warn().attr(\"topic\", topicName).log(\"Forbidden to create topic with an invalid name\");\n            throw new RestException(Status.PRECONDITION_FAILED, e.getMessage());\n        }\n    }\n\n    protected void validatePersistentTopicName(String tenant, String namespace, String encodedTopic) {\n        validateTopicName(tenant, namespace, encodedTopic);\n        if (topicName.getDomain() != TopicDomain.persistent) {\n            throw new RestException(Status.NOT_ACCEPTABLE, \"Need to provide a persistent topic name\");\n        }\n    }\n\n    protected void validatePartitionedTopicName(String tenant, String namespace, String encodedTopic) {\n        // first, it has to be a validate topic name\n        validateTopicName(tenant, namespace, encodedTopic);\n        // second, \"-partition-\" is not allowed\n        if (encodedTopic.contains(TopicName.PARTITIONED_TOPIC_SUFFIX)) {\n            throw new RestException(Status.PRECONDITION_FAILED,\n                    \"Partitioned Topic Name should not contain '-partition-'\");\n        }\n    }\n\n    protected CompletableFuture<Void> validatePartitionedTopicMetadataAsync() {\n        return pulsar().getBrokerService().fetchPartitionedTopicMetadataAsync(topicName)\n                .thenAccept(metadata -> {\n                    if (metadata.partitions < 1) {\n                        throw new RestException(Status.CONFLICT, \"Topic is not partitioned topic\");\n                    }\n                });\n    }\n\n    protected WorkerService validateAndGetWorkerService() {\n        try {\n            return pulsar().getWorkerService();\n        } catch (UnsupportedOperationException e) {\n            throw new RestException(Status.CONFLICT, e.getMessage());","sourceCodeStart":268,"sourceCodeEnd":304,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java#L268-L304","documentation":"Thrown by validatePartitionedTopicName when the topic local name already contains the internal partition suffix '-partition-N'. Partitioned topic names must be the base name; partition members (base-partition-0, base-partition-1, ...) are created automatically by the broker and cannot be addressed when creating a partitioned topic. Returns HTTP 412.","triggerScenarios":"Calling createPartitionedTopic (PUT /admin/v2/persistent/tenant/ns/my-topic-partition-0 with numPartitions) on a name that includes '-partition-' — i.e. trying to partition an individual partition of another topic.","commonSituations":"Scripts iterating a topic list (which includes individual partitions) and calling create-partitioned on each; users copying a partition member name from a client URI or logs; automation that lists topics via topics() and re-creates them as partitioned.","solutions":["Strip the '-partition-N' suffix and use the base topic name when calling the partitioned-topic create API.","Filter out names containing '-partition-' from topic lists before re-creating them as partitioned topics.","If you intended to change partition count of an existing partitioned topic, use updatePartitionedTopic on the base name instead.","Reference the partitioned topic by its base name in clients; producers auto-discover partitions."],"exampleFix":"// before\nadmin.topics().createPartitionedTopic(\"my-topic-partition-0\", 4);\n// after\nadmin.topics().createPartitionedTopic(\"my-topic\", 4);","handlingStrategy":"validation","validationCode":"// Java: strip partition suffix before partitioned-topic operations\nstatic String baseTopicName(String local) {\n    int i = local.lastIndexOf(\"-partition-\");\n    return i > 0 ? local.substring(0, i) : local;\n}","typeGuard":"static boolean isPartitionMember(String local) {\n    return local.matches(\".*-partition-\\\\d+$\");\n}","tryCatchPattern":"try {\n    admin.topics().createPartitionedTopic(base, n);\n} catch (PulsarAdminException e) {\n    if (e.getStatusCode() == 412 && e.getMessage().contains(\"-partition-\")) {\n        base = baseTopicName(base); // retry with base name\n    }\n}","preventionTips":["When iterating topic lists, filter or normalize -partition-N names to their base.","Never address individual partitions via the partitioned-topic admin APIs.","Remember producers reach partitions automatically via the base topic name."],"tags":["http-412","partitioned-topic","validation","admin-api"],"backgroundTag":"invalid-partitioned-topic-name","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"}