apache/pulsar · error · org.apache.pulsar.broker.admin.RestException

Need to provide a persistent topic name

Error message

Need to provide a persistent topic name

What it means

Thrown by validatePersistentTopicName when the addressed topic parses correctly but its domain is non-persistent (or otherwise not TopicDomain.persistent). The REST resource was created for persistent topics only, so a non-persistent:// name is rejected with HTTP 406 Not Acceptable.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:277

     */
    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 name
        validateTopicName(tenant, namespace, encodedTopic);
        // second, "-partition-" is not allowed
        if (encodedTopic.contains(TopicName.PARTITIONED_TOPIC_SUFFIX)) {
            throw new RestException(Status.PRECONDITION_FAILED,
                    "Partitioned Topic Name should not contain '-partition-'");
        }
    }

    protected CompletableFuture<Void> validatePartitionedTopicMetadataAsync() {
        return pulsar().getBrokerService().fetchPartitionedTopicMetadataAsync(topicName)
                .thenAccept(metadata -> {
                    if (metadata.partitions < 1) {
                        throw new RestException(Status.CONFLICT, "Topic is not partitioned topic");

View on GitHub (pinned to 820761864e)

Solutions

  1. Use the /admin/v2/non-persistent/... endpoint family for non-persistent topics instead of /admin/v2/persistent/...
  2. Check the topic URI in your configuration: the domain must be 'persistent' for these endpoints.
  3. If the topic was intended to be persistent, create/access it with the persistent:// domain prefix.
  4. Verify client config keys (e.g. topic names in properties/YAML) don't prefix 'non-persistent://'.

Example fix

// before
admin.topics().getStats("non-persistent://my-tenant/my-ns/orders");
// after
admin.topics().getStats("persistent://my-tenant/my-ns/orders");
Defensive patterns

Strategy: validation

Validate before calling

// Java: ensure domain matches the endpoint family
boolean isPersistent(String fq) { return fq.startsWith("persistent://"); }
// only call admin.topics() (persistent endpoints) if isPersistent(fq)

Type guard

static String toPersistentUri(String fq) {
    return fq.startsWith("non-persistent://")
        ? fq.replace("non-persistent://", "persistent://") : fq;
}

Try / catch

try {
    admin.topics().getStats(fqTopic);
} catch (PulsarAdminException e) {
    if (e.getStatusCode() == 406) {
        // wrong domain: reroute to non-persistent API or fix the URI
    }
}

Prevention

When it happens

Trigger: Calling a persistent-topic admin endpoint (e.g. GET /admin/v2/persistent/tenant/ns/topic stats or subscriptions) while passing the topic under a path or with a name resolving to domain non-persistent, e.g. mixing up /admin/v2/persistent/ with a topic that actually is non-persistent://tenant/ns/topic.

Common situations: Applications configured with 'non-persistent' topic URIs but using persistent admin endpoints; copy-paste of topic URIs between persistent and non-persistent sections of admin tooling; clients that omit the scheme and get a default mismatch.

Related errors


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