apache/pulsar · error · IllegalArgumentException

V1 topic names (with cluster component) are no longer suppor

Error message

V1 topic names (with cluster component) are no longer supported. Please use the V2 format: '<domain>://tenant/namespace/topic'. Got: ${topic}

What it means

toFullTopicName validates/normalizes a topic string to the full V2 form. Pulsar dropped support for V1 topic names, which included a cluster segment ('domain://tenant/cluster/namespace/topic'). If the string after '://' splits into exactly 4 segments (i.e. it still has the cluster component), the method throws this IllegalArgumentException directing the user to the V2 format.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java:555

    }

    /**
     * Convert a topic name to a full topic name.
     * In Pulsar, a full topic name is "<domain>://<tenant>/<namespace>/<local-topic>".
     * For convenience, clients can pass a short topic name:
     * - "<local-topic>", which represents "persistent://public/default/<local-topic>"
     * - "<tenant>/<namespace>/<local-topic>", which represents "persistent://<tenant>/<namespace>/<local-topic>"
     *
     * @param topic the topic name from client
     * @return the full topic name.
     */
    public static String toFullTopicName(String topic) {
        final int index = topic.indexOf("://");
        if (index >= 0) {
            TopicDomain.getEnum(topic.substring(0, index));
            final List<String> parts = splitBySlash(topic.substring(index + "://".length()), 4);
            if (parts.size() == 4) {
                throw new IllegalArgumentException(
                        "V1 topic names (with cluster component) are no longer supported. "
                        + "Please use the V2 format: '<domain>://tenant/namespace/topic'. Got: " + topic);
            }
            if (parts.size() != 3) {
                throw new IllegalArgumentException(topic + " is invalid. "
                    + "Expected format: '<domain>://tenant/namespace/topic'");
            }
            NamespaceName.validateNamespaceName(parts.get(0), parts.get(1));
            if (StringUtils.isBlank(parts.get(2))) {
                throw new IllegalArgumentException(topic + " has blank local topic");
            }
            return topic; // it's a valid full topic name
        } else {
            List<String> parts = splitBySlash(topic, 0);
            if (parts.size() != 1 && parts.size() != 3) {
                throw new IllegalArgumentException(topic + " is invalid");
            }
            if (parts.size() == 1) {

View on GitHub (pinned to 820761864e)

Solutions

  1. Remove the cluster segment: convert 'domain://tenant/cluster/namespace/topic' to 'domain://tenant/namespace/topic'
  2. Update legacy configs/scripts/clients to the V2 format
  3. If cluster information matters, use V2 namespaces (tenant/namespace) which encode the cluster policy differently

Example fix

// before
TopicName.toFullTopicName("persistent://my-tenant/us-west/my-ns/my-topic");
// after
TopicName.toFullTopicName("persistent://my-tenant/my-ns/my-topic");
Defensive patterns

Strategy: validation

Validate before calling

public static void requireNoClusterSegment(String topic) {
    int i = topic.indexOf("://");
    if (i >= 0 && topic.substring(i + 3).split("/").length == 4) {
        throw new IllegalArgumentException("V1 topic name with cluster segment: " + topic);
    }
}

Type guard

public static boolean isV2TopicName(String s) {
    int i = s == null ? -1 : s.indexOf("://");
    return i >= 0 && s.substring(i + 3).split("/").length == 3;
}

Try / catch

try {
    String full = TopicName.toFullTopicName(legacyTopic);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("V1 topic names")) {
        String[] p = legacyTopic.substring(legacyTopic.indexOf("://") + 3).split("/");
        legacyTopic = legacyTopic.substring(0, legacyTopic.indexOf("://") + 3) + p[0] + "/" + p[2] + "/" + p[3]; // drop cluster
    }
}

Prevention

When it happens

Trigger: Calling TopicName.toFullTopicName() (or APIs that route through it) with a legacy V1 name like 'persistent://my-tenant/us-west/my-ns/my-topic' — 4 path segments after the domain.

Common situations: Migrating old Pulsar 1.x configurations, scripts, or clients to Pulsar 2.x+; old documentation/bookmarks; data exported from a legacy cluster still carrying cluster-scoped topic names.

Related errors


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