apache/pulsar · error · IllegalArgumentException

Invalid short topic name '${completeTopicName}', it should b

Error message

Invalid short topic name '${completeTopicName}', it should be in the format of <tenant>/<namespace>/<topic> or <topic>

What it means

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.

Source

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

            // The topic name can be in two different forms, one is fully qualified topic name,
            // the other one is short topic name
            int index = completeTopicName.indexOf("://");
            if (index < 0) {
                // The short topic name can be:
                // - <topic>
                // - <tenant>/<namespace>/<topic>
                List<String> parts = splitBySlash(completeTopicName, 0);
                this.domain = TopicDomain.persistent;
                if (parts.size() == 3) {
                    this.tenant = parts.get(0);
                    this.namespacePortion = parts.get(1);
                    this.localName = parts.get(2);
                } else if (parts.size() == 1) {
                    this.tenant = PUBLIC_TENANT;
                    this.namespacePortion = DEFAULT_NAMESPACE;
                    this.localName = parts.get(0);
                } else {
                    throw new IllegalArgumentException(
                        "Invalid short topic name '" + completeTopicName + "', it should be in the format of "
                        + "<tenant>/<namespace>/<topic> or <topic>");
                }
                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. "

View on GitHub (pinned to 820761864e)

Solutions

  1. Use the full topic only ('my-topic') or the full 'tenant/namespace/topic' form
  2. Add the missing or remove the extra '/' segment so the name has exactly 1 or 3 slash-separated parts
  3. Use the fully-qualified form 'persistent://tenant/namespace/topic' to be unambiguous
  4. Trim whitespace and strip leading/trailing slashes before passing the name

Example fix

// before
TopicName.get("tenant/namespace");
// after
TopicName.get("persistent://tenant/namespace/my-topic");
Defensive patterns

Strategy: validation

Validate before calling

static boolean isShortTopicNameValid(String name) {
    if (name == null || name.isEmpty()) return false;
    String[] parts = name.split("/", -1);
    return parts.length == 1 || (parts.length == 3 && parts[0].length() > 0
        && parts[1].length() > 0 && parts[2].length() > 0);
}

Type guard

static boolean isUsableTopicName(String name) {
    return name != null && (name.contains("://") || isShortTopicNameValid(name));
}

Try / catch

try {
    TopicName tn = TopicName.get(userTopic);
} catch (IllegalArgumentException e) {
    log.warn("Rejected topic name '{}': {}", userTopic, e.getMessage());
    throw new InvalidTopicNameException(userTopic);
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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