apache/pulsar · error · IllegalArgumentException
Invalid topic name ${completeTopicName}
Error message
Invalid topic name ${completeTopicName} What it means
When a fully-qualified name with a 'domain://' scheme is parsed, the path after the scheme must contain exactly 3 slash-separated parts: tenant, namespace portion, and topic local name. Fewer parts (e.g. 'persistent://tenant/ns') or more than the allowed split yields this generic IllegalArgumentException. Note 4 parts instead raises the dedicated V1 error.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/naming/TopicName.java:192
this.segmentRange = null;
this.segmentId = -1;
this.completeTopicName = domain.name() + "://" + tenant + "/" + namespacePortion + "/" + localName;
} else {
this.domain = TopicDomain.getEnum(completeTopicName.substring(0, index));
// Scalable topic domains (topic://, segment://) only support the new format
// and local names may contain '/', so use limit(3) to keep the rest as localName.
boolean isScalableDomain = this.domain == TopicDomain.topic
|| this.domain == TopicDomain.segment;
int splitLimit = isScalableDomain ? 3 : 4;
List<String> parts = splitBySlash(completeTopicName.substring(index + "://".length()),
splitLimit);
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: "
+ completeTopicName);
} else if (parts.size() != 3) {
throw new IllegalArgumentException("Invalid topic name " + completeTopicName);
}
this.tenant = parts.get(0);
this.namespacePortion = parts.get(1);
String rawLocalName = parts.get(2);
// For segment:// domains, split local name into parent topic name + descriptor
if (this.domain == TopicDomain.segment) {
int lastSlash = rawLocalName.lastIndexOf('/');
if (lastSlash <= 0) {
throw new IllegalArgumentException(
"Invalid segment topic name: local name must contain"
+ " '<parent-topic>/<hashStart>-<hashEnd>-<segmentId>'. Got: "
+ completeTopicName);
}
this.localName = rawLocalName.substring(0, lastSlash);
String descriptor = rawLocalName.substring(lastSlash + 1);
String[] descParts = descriptor.split("-");
if (descParts.length != 3) {View on GitHub (pinned to 820761864e)
Solutions
- Supply the complete 'domain://tenant/namespace/topic' string including the local topic name
- Check the source of the string (config, env var, API response) for truncation or wrong variable
- If you have a namespace not a topic, use NamespaceName/NamespaceName.get instead of TopicName
Example fix
// before
TopicName.get("persistent://my-tenant/my-ns");
// after
TopicName.get("persistent://my-tenant/my-ns/my-topic"); Defensive patterns
Strategy: validation
Validate before calling
static boolean isQualifiedTopicNameValid(String name) {
if (name == null) return false;
int i = name.indexOf("://");
if (i < 0) return false;
String[] parts = name.substring(i + 3).split("/", -1);
return parts.length == 3 && java.util.Arrays.stream(parts).allMatch(p -> !p.isEmpty());
} Type guard
static boolean isCompleteTopicName(String name) {
return name != null && isQualifiedTopicNameValid(name);
} Try / catch
try {
TopicName tn = TopicName.get(topic);
} catch (IllegalArgumentException e) {
log.error("Malformed topic name '{}'", topic, e);
throw new IllegalArgumentException("Provide domain://tenant/namespace/topic, got: " + topic);
} Prevention
- Log the raw input string when topic parsing fails to spot truncation sources
- Use TopicName/TopicName.get early at config-load time so bad values fail fast
- Distinguish namespace names from topic names in your data model; use NamespaceName for namespaces
- Avoid storing partially-built names; always store the complete 'domain://tenant/ns/topic'
When it happens
Trigger: Calling TopicName.get('persistent://tenant') or TopicName.get('persistent://tenant/ns/') — scheme present but path does not split into exactly tenant/namespace/topic.
Common situations: Truncated topic names in configs or logs; passing a namespace name ('persistent://tenant/namespace') where a topic is expected; programmatic string building that lost the topic segment; empty topic after a trailing slash.
Related errors
- Invalid short topic name '${completeTopicName}', it should b
- V1 topic names (with cluster component) are no longer suppor
- Invalid segment topic name: local name must contain '<parent
- Invalid segment descriptor: expected '<hexStart>-<hexEnd>-<s
- ResourceGroupCreate: Invalid null ResourceGroup config
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/2a71753638babc98.
Report an issue: GitHub.