{"record":{"id":"bdf9a5545a8d2705","repo":"apache/pulsar","slug":"v1-topic-names-with-cluster-component-are-no-lon","errorCode":null,"errorMessage":"V1 topic names (with cluster component) are no longer supported. Please use the V2 format: '<domain>://tenant/namespace/topic'. Got: ${completeTopicName}","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":187,"sourceCode":"                } else {\n                    throw new IllegalArgumentException(\n                        \"Invalid short topic name '\" + completeTopicName + \"', it should be in the format of \"\n                        + \"<tenant>/<namespace>/<topic> or <topic>\");\n                }\n                this.segmentRange = null;\n                this.segmentId = -1;\n                this.completeTopicName = domain.name() + \"://\" + tenant + \"/\" + namespacePortion + \"/\" + localName;\n            } else {\n                this.domain = TopicDomain.getEnum(completeTopicName.substring(0, index));\n                // Scalable topic domains (topic://, segment://) only support the new format\n                // and local names may contain '/', so use limit(3) to keep the rest as localName.\n                boolean isScalableDomain = this.domain == TopicDomain.topic\n                        || this.domain == TopicDomain.segment;\n                int splitLimit = isScalableDomain ? 3 : 4;\n                List<String> parts = splitBySlash(completeTopicName.substring(index + \"://\".length()),\n                        splitLimit);\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: \"\n                                    + completeTopicName);\n                } else if (parts.size() != 3) {\n                    throw new IllegalArgumentException(\"Invalid topic name \" + completeTopicName);\n                }\n                this.tenant = parts.get(0);\n                this.namespacePortion = parts.get(1);\n                String rawLocalName = parts.get(2);\n\n                // For segment:// domains, split local name into parent topic name + descriptor\n                if (this.domain == TopicDomain.segment) {\n                    int lastSlash = rawLocalName.lastIndexOf('/');\n                    if (lastSlash <= 0) {\n                        throw new IllegalArgumentException(\n                                \"Invalid segment topic name: local name must contain\"\n                                        + \" '<parent-topic>/<hashStart>-<hashEnd>-<segmentId>'. Got: \"\n                                        + completeTopicName);","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java#L169-L205","documentation":"Pulsar V1 topic names embedded a cluster component: 'domain://cluster/tenant/namespace/topic' (4 path parts after '://'). The parser rejects this shape outright and asks callers to migrate to the V2 format 'domain://tenant/namespace/topic'. It is thrown from the TopicName constructor whenever a fully-qualified name splits into 4 parts after the scheme.","triggerScenarios":"Calling TopicName.get('persistent://clusterX/tenant/ns/topic') or any API that resolves a topic name containing a legacy cluster segment after '://'.","commonSituations":"Migrating from very old Pulsar clusters (pre-2.x V1 naming); configs, scripts, or bookmarked topic URLs copied from an old deployment; broker/standalone configs carried over across a version upgrade.","solutions":["Remove the cluster component: 'persistent://clusterX/tenant/ns/topic' -> 'persistent://tenant/ns/topic'","Re-derive topic names from current cluster config instead of legacy stored values","If reading legacy data, map old names to V2 names with a migration/shim before calling TopicName.get"],"exampleFix":"// before\nTopicName topic = TopicName.get(\"persistent://us-west/my-tenant/my-ns/my-topic\");\n// after\nTopicName topic = TopicName.get(\"persistent://my-tenant/my-ns/my-topic\");","handlingStrategy":"validation","validationCode":"static String stripV1Cluster(String topic) {\n    java.util.regex.Matcher m = java.util.regex.Pattern\n        .compile(\"^([a-z]+)://[^/]+/([^/]+/[^/]+/[^/]+)$\").matcher(topic);\n    if (m.matches()) {\n        throw new IllegalArgumentException(\"V1 topic name not supported: \" + topic);\n    }\n    return topic;\n}","typeGuard":"static boolean isV2TopicName(String name) {\n    java.util.regex.Matcher m = java.util.regex.Pattern\n        .compile(\"^[a-z]+://[^/]+/[^/]+/[^/]+$\").matcher(name == null ? \"\" : name);\n    return m.matches();\n}","tryCatchPattern":"try {\n    TopicName tn = TopicName.get(topic);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"V1 topic names\")) {\n        topic = migrateV1ToV2(topic); // drop cluster component, retry once\n        TopicName tn = TopicName.get(topic);\n    } else throw e;\n}","preventionTips":["Search configs/scripts for legacy 'domain://cluster/tenant/ns/topic' patterns after upgrading from Pulsar 1.x","Store topic names in the V2 form going forward","Add a startup check that rejects V1-format names in configuration","Regenerate topic URLs from current cluster metadata rather than copying old ones"],"tags":["java","illegal-argument","v1-legacy","topic-name","pulsar"],"backgroundTag":"deprecated-name-format","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"}