apache/pulsar · error · IllegalArgumentException

Invalid topic name: ${completeTopicName}

Error message

Invalid topic name: ${completeTopicName}

What it means

When parsing the topic name throws a NullPointerException internally (e.g. a null or badly-structured input where an expected segment is absent), TopicName's constructor catches the NPE and rethrows it as an IllegalArgumentException with the message 'Invalid topic name: <input>', preserving the NPE as the cause. It exists so callers get a consistent, meaningful validation error instead of a raw NPE.

Source

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

                }

                if (this.domain == TopicDomain.segment) {
                    this.completeTopicName = String.format("%s://%s/%s/%s/%s",
                            domain, tenant, namespacePortion, localName,
                            String.format("%04x-%04x-%d", segmentRange.start(), segmentRange.end(), segmentId));
                } else {
                    this.completeTopicName = completeTopicName;
                }
            }

            if (StringUtils.isBlank(localName)) {
                throw new IllegalArgumentException(String.format("Invalid topic name: %s. Topic local name must not"
                        + " be blank.", completeTopicName));
            }
            this.partitionIndex = getPartitionIndex(localName);
            this.namespaceName = NamespaceName.get(tenant, namespacePortion);
        } catch (NullPointerException e) {
            throw new IllegalArgumentException("Invalid topic name: " + completeTopicName, e);
        }
    }

    public boolean isPersistent() {
        return TopicDomain.persistent == domain || TopicDomain.topic == domain || TopicDomain.segment == domain;
    }

    public boolean isScalable() {
        return TopicDomain.topic == domain;
    }

    public boolean isSegment() {
        return TopicDomain.segment == domain;
    }

    /**
     * Get the segment hash range for segment:// topics.
     *

View on GitHub (pinned to 820761864e)

Solutions

  1. Log/inspect the exception cause (getCause()) to see the underlying NPE and which segment was missing
  2. Ensure the input is non-null and follows '<domain>://<tenant>/<namespace>/<local-name>'
  3. Add an explicit null/format check in your code before calling TopicName.get()

Example fix

// before
TopicName.get(config.getTopic()); // getTopic() may return null
// after
String t = config.getTopic();
if (t == null || !t.contains("://")) { throw new ConfigException("topic must be a full topic URL"); }
TopicName.get(t);
Defensive patterns

Strategy: validation

Validate before calling

public static void requireNotNullTopic(String topic) {
    java.util.Objects.requireNonNull(topic, "topic name must not be null");
    if (!topic.contains("://")) throw new IllegalArgumentException("topic must be a full topic URL: " + topic);
}

Type guard

public static boolean isNonNullFullTopic(String s) { return s != null && s.contains("://"); }

Try / catch

try {
    TopicName tn = TopicName.get(input);
} catch (IllegalArgumentException e) {
    Throwable cause = e.getCause(); // NPE shows which parse step failed
    log.error("Invalid topic '{}': {}", input, cause == null ? e.getMessage() : cause.toString());
}

Prevention

When it happens

Trigger: Calling TopicName.get() / new TopicName(...) with a null or malformed string that causes a NullPointerException during parsing — e.g. null input, or a name that fails the expected 'domain://tenant/namespace/local' structure so an expected part is missing.

Common situations: Passing an unvalidated config value or method argument that is null; a topic name with too few segments causing an array/list index to be missing; test fixtures with placeholder nulls; deserialization producing null topic fields.

Related errors


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