apache/pulsar · warning · RestException

Topic name is not valid

Error message

Topic name is not valid

What it means

HTTP 412 PRECONDITION_FAILED from validateTopicName: the encoded topic string supplied to a transaction admin endpoint could not be parsed into a valid topic (CompleteTopicName/TopicName parsing threw). The broker logs 'Failed to validate topic name' with tenant/namespace/topic details.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/TransactionsBase.java:541

           throw new RestException(SERVICE_UNAVAILABLE,
                    "This Broker is not configured with transactionCoordinatorEnabled=true.");
        }
    }

    protected void validateTopicName(String tenant, String namespace, String encodedTopic) {
        String topic = Codec.decode(encodedTopic);
        try {
            this.namespaceName = NamespaceName.get(tenant, namespace);
            this.topicName = TopicName.get(TopicDomain.persistent.toString(), namespaceName, topic);
        } catch (IllegalArgumentException e) {
            log.warn()
                    .attr("domain", domain())
                    .attr("tenant", tenant)
                    .attr("namespace", namespace)
                    .attr("topic", topic)
                    .exception(e)
                    .log("Failed to validate topic name");
            throw new RestException(Response.Status.PRECONDITION_FAILED, "Topic name is not valid");
        }
    }

    protected CompletableFuture<Void> internalScaleTransactionCoordinators(int replicas) {
        return validateSuperUserAccessAsync()
                .thenCompose((ignore) -> namespaceResources().getPartitionedTopicResources()
                        .updatePartitionedTopicAsync(SystemTopicNames.TRANSACTION_COORDINATOR_ASSIGN, p -> {
                            if (p.partitions >= replicas) {
                                throw new RestException(Response.Status.NOT_ACCEPTABLE,
                                        "Number of transaction coordinators should "
                                                + "be more than the current number of transaction coordinator");
                            }
                            return new PartitionedTopicMetadata(replicas);
                        }));
    }

    protected CompletableFuture<PositionInPendingAckStats> internalGetPositionStatsPendingAckStats(
            boolean authoritative, String subName, Position position, Integer batchIndex) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Use the fully qualified topic name: persistent://tenant/namespace/topic (or non-persistent://...), URL-encoded when in a path.
  2. Validate the topic name with TopicName.get(topic) client-side or org.apache.pulsar.common.naming utilities before calling the API.
  3. Check for encoding problems: encode '/' as %2F if the endpoint expects a single path segment, or use the query/REST variant that takes separate tenant/ns/topic.
  4. Remove any partition suffix if the endpoint expects the base topic, or add it if it expects the specific partition.

Example fix

// before
String topic = "my-topic"; // not fully qualified
admin.transactions().getPendingAckStats("tenant", "ns", topic);
// after
String topic = "persistent://tenant/ns/my-topic";
admin.transactions().getPendingAckStats("tenant", "ns", topic);
Defensive patterns

Strategy: validation

Validate before calling

import org.apache.pulsar.common.naming.TopicName;

static boolean isValidTopicName(String topic) {
    try {
        TopicName.get(topic);
        return true;
    } catch (Exception e) {
        return false;
    }
}
// use: isValidTopicName("persistent://tenant/ns/my-topic")

Type guard

static boolean isFullyQualifiedTopic(String topic) {
    return topic != null && (topic.startsWith("persistent://") || topic.startsWith("non-persistent://"))
        && topic.split("://", 2)[1].split("/").length >= 3;
}

Try / catch

try {
    admin.transactions().getPendingAckStats(tenant, ns, topic);
} catch (PulsarAdminException e) {
    if (e.getStatusCode() == 412 && e.getMessage().contains("Topic name is not valid")) {
        String fqTopic = "persistent://" + tenant + "/" + ns + "/" + topic.replaceFirst("^persistent://[^/]+/[^/]+/", "");
        // retry with fully qualified, URL-encoded name
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Transaction admin calls (e.g. transaction pending-ack stats, coordinator endpoints taking a topic parameter) where the topic argument is malformed: missing persistent/non-persistent domain, missing local-name, bad tenant/namespace segment, or not URL-encoded special characters.

Common situations: Passing a short topic name (topic-only) where a fully-qualified persistent://tenant/namespace/topic is expected; URL-encoding issues with '/' in path parameters; partitioned vs non-partitioned confusion (appending -partition-N); mixing up tenants/namespaces in the topic string.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/b8d2cee42d957778. Report an issue: GitHub.