apache/pulsar · error · org.apache.pulsar.broker.admin.RestException
Cannot create topic in system topic format!
Error message
Cannot create topic in system topic format!
What it means
Thrown by validateCreateTopic when a client attempts to create a topic whose name collides with an internal system topic (transaction coordinator internal topics such as persistent://<ns>/__transaction_coordinator_assign or __transaction_buffer_snapshot style names) or otherwise violates creation-name rules. Pulsar reserves these names for internal machinery and rejects creation with HTTP 400/412.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:264
.log("Invalid topic name");
throw new RestException(Status.PRECONDITION_FAILED, "Topic name is not valid");
}
}
/**
* Validates that a topic can be created.
*
* <p>This is the single source of truth for topic-creation name validation shared by every admin create
* endpoint (persistent, non-persistent and scalable topics). Rejecting here keeps topics which could never be
* reached (e.g. because clients trim topic names) from being created. The transaction-internal-name rule is
* gated on {@link TopicDomain#persistent} so it stays specific to persistent topics, while the whitespace
* validation applies uniformly to all topic types.
*/
protected void validateCreateTopic(TopicName topicName) {
if (topicName.getDomain() == TopicDomain.persistent
&& SystemTopicNames.isTransactionInternalName(topicName)) {
log.warn().attr("topic", topicName).log("Forbidden to create transaction internal topic");
throw new RestException(Status.BAD_REQUEST, "Cannot create topic in system topic format!");
}
try {
TopicName.validateTopicNameForCreation(topicName);
} catch (IllegalArgumentException e) {
log.warn().attr("topic", topicName).log("Forbidden to create topic with an invalid name");
throw new RestException(Status.PRECONDITION_FAILED, e.getMessage());
}
}
protected void validatePersistentTopicName(String tenant, String namespace, String encodedTopic) {
validateTopicName(tenant, namespace, encodedTopic);
if (topicName.getDomain() != TopicDomain.persistent) {
throw new RestException(Status.NOT_ACCEPTABLE, "Need to provide a persistent topic name");
}
}
protected void validatePartitionedTopicName(String tenant, String namespace, String encodedTopic) {
// first, it has to be a validate topic nameView on GitHub (pinned to 820761864e)
Solutions
- Exclude topics whose local name starts with the reserved system-topic prefixes (e.g. __transaction_) from your creation script.
- Skip system topics when enumerating source topics: filter with SystemTopicNames.isTransactionInternalName or equivalent before create.
- Choose a different topic name for your application data.
- Do not manually manage transaction internal topics; they are created automatically when transactions are enabled.
Example fix
// before
for (String t : exportedTopics) {
admin.topics().createNonPartitionedTopic(t); // fails on __transaction_coordinator_assign
}
// after
for (String t : exportedTopics) {
if (!t.contains("__transaction_")) {
admin.topics().createNonPartitionedTopic(t);
}
} Defensive patterns
Strategy: validation
Validate before calling
// Java: skip reserved system topic names before creating
String[] reservedPrefixes = {"__transaction_", "__change_events", "__compaction"};
boolean isReserved(String local) {
return local.startsWith("__transaction_");
} Type guard
static boolean isUserTopic(String fqTopic) {
String local = fqTopic.substring(fqTopic.lastIndexOf('/') + 1);
return !local.startsWith("__transaction_");
} Try / catch
try {
admin.topics().createNonPartitionedTopic(topic);
} catch (PulsarAdminException e) {
if (e.getStatusCode() == 400 || e.getStatusCode() == 412) {
log.warn("Skipping reserved/invalid topic name: {}", topic); // don't fail the batch
}
} Prevention
- Filter SystemTopicNames-prefixed topics out of any topic-replay/migration script.
- Treat names starting with '__' as broker-reserved in your naming convention.
- Never hand-create transaction coordinator topics; enable transactions instead.
- Document reserved prefixes for your platform users.
When it happens
Trigger: Explicitly creating a persistent topic whose name matches SystemTopicNames.isTransactionInternalName, e.g. PUT /admin/v2/persistent/my-tenant/my-ns/__transaction_coordinator_assign; or creating a topic name rejected by TopicName.validateTopicNameForCreation (412).
Common situations: Scripts replaying topic lists captured from a namespace that include system topics; tooling migrating topics between clusters and blindly recreating everything; users inventing topic names that collide with reserved transaction-internal names.
Related errors
- Input bundles do not cover the whole hash range. first:
- Namespace name is not valid
- Tenant name or namespace is not valid
- Topic name is not valid
- Need to provide a persistent topic name
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/0329c653a8301348.
Report an issue: GitHub.