{"record":{"id":"8114f9038a944589","repo":"apache/pulsar","slug":"invalid-short-topic-name-completetopicname-i","errorCode":null,"errorMessage":"Invalid short topic name '${completeTopicName}', it should be in the format of <tenant>/<namespace>/<topic> or <topic>","messagePattern":"Invalid short topic name '(.+?)', it should be in the format of <tenant>/<namespace>/<topic> or <topic>","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java","lineNumber":170,"sourceCode":"            // The topic name can be in two different forms, one is fully qualified topic name,\n            // the other one is short topic name\n            int index = completeTopicName.indexOf(\"://\");\n            if (index < 0) {\n                // The short topic name can be:\n                // - <topic>\n                // - <tenant>/<namespace>/<topic>\n                List<String> parts = splitBySlash(completeTopicName, 0);\n                this.domain = TopicDomain.persistent;\n                if (parts.size() == 3) {\n                    this.tenant = parts.get(0);\n                    this.namespacePortion = parts.get(1);\n                    this.localName = parts.get(2);\n                } else if (parts.size() == 1) {\n                    this.tenant = PUBLIC_TENANT;\n                    this.namespacePortion = DEFAULT_NAMESPACE;\n                    this.localName = parts.get(0);\n                } 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. \"","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java#L152-L188","documentation":"TopicName.get()/the TopicName constructor parses a topic name given without a 'domain://' scheme (a 'short topic name'). Only two shapes are accepted: '<topic>' alone (defaults to public/default namespace, persistent domain) or the full '<tenant>/<namespace>/<topic>'. Any other slash-count (2 parts, 4+ parts, empty segments producing extra parts) makes the parser throw this IllegalArgumentException because the name is ambiguous.","triggerScenarios":"Calling TopicName.get(name) or any API accepting a topic string with a name like 'tenant/namespace' (missing topic), 'tenant/namespace/topic/partition' style with extra slashes, or 'my//topic' (empty segment).","commonSituations":"Config files or CLI flags where users supply 'tenant/namespace' without the topic; string concatenation that leaves a trailing slash; copying a namespace (not topic) from the UI; splitting/parsing code that drops the domain prefix but keeps V1 4-part names.","solutions":["Use the full topic only ('my-topic') or the full 'tenant/namespace/topic' form","Add the missing or remove the extra '/' segment so the name has exactly 1 or 3 slash-separated parts","Use the fully-qualified form 'persistent://tenant/namespace/topic' to be unambiguous","Trim whitespace and strip leading/trailing slashes before passing the name"],"exampleFix":"// before\nTopicName.get(\"tenant/namespace\");\n// after\nTopicName.get(\"persistent://tenant/namespace/my-topic\");","handlingStrategy":"validation","validationCode":"static boolean isShortTopicNameValid(String name) {\n    if (name == null || name.isEmpty()) return false;\n    String[] parts = name.split(\"/\", -1);\n    return parts.length == 1 || (parts.length == 3 && parts[0].length() > 0\n        && parts[1].length() > 0 && parts[2].length() > 0);\n}","typeGuard":"static boolean isUsableTopicName(String name) {\n    return name != null && (name.contains(\"://\") || isShortTopicNameValid(name));\n}","tryCatchPattern":"try {\n    TopicName tn = TopicName.get(userTopic);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Rejected topic name '{}': {}\", userTopic, e.getMessage());\n    throw new InvalidTopicNameException(userTopic);\n}","preventionTips":["Always pass fully-qualified 'persistent://tenant/namespace/topic' names in configs and code","Validate slash-count before constructing TopicName","Trim whitespace and strip leading/trailing slashes from user input","Never build topic names by raw string concatenation without a helper that normalizes the format"],"tags":["java","illegal-argument","topic-name","pulsar"],"backgroundTag":"invalid-topic-name","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}