{"record":{"id":"019fd14af114f340","repo":"apache/pulsar","slug":"v1-topic-names-with-cluster-component-are-no-lon-019fd1","errorCode":null,"errorMessage":"V1 topic names (with cluster component) are no longer supported. Please use the V2 format: '<domain>://tenant/namespace/topic'. Got: ${topic}","messagePattern":"V1 topic names \\(with cluster component\\) are no longer supported\\. Please use the V2 format: '<domain>://tenant/namespace/topic'\\. Got: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java","lineNumber":555,"sourceCode":"    }\n\n    /**\n     * Convert a topic name to a full topic name.\n     * In Pulsar, a full topic name is \"<domain>://<tenant>/<namespace>/<local-topic>\".\n     * For convenience, clients can pass a short topic name:\n     * - \"<local-topic>\", which represents \"persistent://public/default/<local-topic>\"\n     * - \"<tenant>/<namespace>/<local-topic>\", which represents \"persistent://<tenant>/<namespace>/<local-topic>\"\n     *\n     * @param topic the topic name from client\n     * @return the full topic name.\n     */\n    public static String toFullTopicName(String topic) {\n        final int index = topic.indexOf(\"://\");\n        if (index >= 0) {\n            TopicDomain.getEnum(topic.substring(0, index));\n            final List<String> parts = splitBySlash(topic.substring(index + \"://\".length()), 4);\n            if (parts.size() == 4) {\n                throw new IllegalArgumentException(\n                        \"V1 topic names (with cluster component) are no longer supported. \"\n                        + \"Please use the V2 format: '<domain>://tenant/namespace/topic'. Got: \" + topic);\n            }\n            if (parts.size() != 3) {\n                throw new IllegalArgumentException(topic + \" is invalid. \"\n                    + \"Expected format: '<domain>://tenant/namespace/topic'\");\n            }\n            NamespaceName.validateNamespaceName(parts.get(0), parts.get(1));\n            if (StringUtils.isBlank(parts.get(2))) {\n                throw new IllegalArgumentException(topic + \" has blank local topic\");\n            }\n            return topic; // it's a valid full topic name\n        } else {\n            List<String> parts = splitBySlash(topic, 0);\n            if (parts.size() != 1 && parts.size() != 3) {\n                throw new IllegalArgumentException(topic + \" is invalid\");\n            }\n            if (parts.size() == 1) {","sourceCodeStart":537,"sourceCodeEnd":573,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java#L537-L573","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the cluster segment: convert 'domain://tenant/cluster/namespace/topic' to 'domain://tenant/namespace/topic'","Update legacy configs/scripts/clients to the V2 format","If cluster information matters, use V2 namespaces (tenant/namespace) which encode the cluster policy differently"],"exampleFix":"// before\nTopicName.toFullTopicName(\"persistent://my-tenant/us-west/my-ns/my-topic\");\n// after\nTopicName.toFullTopicName(\"persistent://my-tenant/my-ns/my-topic\");","handlingStrategy":"validation","validationCode":"public static void requireNoClusterSegment(String topic) {\n    int i = topic.indexOf(\"://\");\n    if (i >= 0 && topic.substring(i + 3).split(\"/\").length == 4) {\n        throw new IllegalArgumentException(\"V1 topic name with cluster segment: \" + topic);\n    }\n}","typeGuard":"public static boolean isV2TopicName(String s) {\n    int i = s == null ? -1 : s.indexOf(\"://\");\n    return i >= 0 && s.substring(i + 3).split(\"/\").length == 3;\n}","tryCatchPattern":"try {\n    String full = TopicName.toFullTopicName(legacyTopic);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"V1 topic names\")) {\n        String[] p = legacyTopic.substring(legacyTopic.indexOf(\"://\") + 3).split(\"/\");\n        legacyTopic = legacyTopic.substring(0, legacyTopic.indexOf(\"://\") + 3) + p[0] + \"/\" + p[2] + \"/\" + p[3]; // drop cluster\n    }\n}","preventionTips":["Migrate all Pulsar 1.x configs/scripts to V2 names before upgrading clients","Search configs for topic URLs with 4 segments after '://'","Remember the cluster field was removed in Pulsar 2.0; namespaces replace it"],"tags":["java","topic-naming","backward-compatibility","v1-migration"],"backgroundTag":"v1-topic-name-unsupported","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}