apache/pulsar · error · IllegalArgumentException

Invalid segment topic name: local name must contain '<parent

Error message

Invalid segment topic name: local name must contain '<parent-topic>/<hashStart>-<hashEnd>-<segmentId>'. Got: ${completeTopicName}

What it means

For the segment:// domain, the topic local name must end with a '/'-separated segment descriptor appended to the parent topic name: '<parent-topic>/<hashStart>-<hashEnd>-<segmentId>'. If there is no slash or the slash is at position 0 (no parent topic), TopicName throws this IllegalArgumentException.

Source

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

                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) {
                        throw new IllegalArgumentException(
                                "Invalid segment descriptor: expected '<hexStart>-<hexEnd>-<segmentId>',"
                                        + " got: '" + descriptor + "'");
                    }
                    this.segmentRange = HashRange.of(
                            Integer.parseInt(descParts[0], 16),
                            Integer.parseInt(descParts[1], 16));
                    this.segmentId = Long.parseLong(descParts[2]);
                } else {
                    this.localName = rawLocalName;

View on GitHub (pinned to 820761864e)

Solutions

  1. Append a valid descriptor: 'segment://tenant/ns/parent-topic/0000-ffff-0'
  2. Ensure the local name contains both a parent topic AND '/' + descriptor (lastIndexOf('/') must be > 0)
  3. If you meant a regular topic, use topic:// or persistent:// domain instead of segment://

Example fix

// before
TopicName.get("segment://tenant/ns/my-topic");
// after
TopicName.get("segment://tenant/ns/my-topic/0000-ffff-0");
Defensive patterns

Strategy: validation

Validate before calling

static boolean isSegmentTopicNameValid(String name) {
    if (name == null || !name.startsWith("segment://")) return false;
    String path = name.substring("segment://".length());
    String[] parts = path.split("/", -1);
    if (parts.length < 4) return false; // tenant/ns/parent-topic.../descriptor
    String descriptor = parts[parts.length - 1];
    int lastSlash = path.lastIndexOf('/');
    return lastSlash > 0 && descriptor.matches("[0-9a-fA-F]+-[0-9a-fA-F]+-\\d+");
}

Type guard

static boolean hasSegmentDescriptor(String segmentTopic) {
    return segmentTopic != null && segmentTopic.lastIndexOf('/') > segmentTopic.indexOf("://") + 2;
}

Try / catch

try {
    TopicName tn = TopicName.get(segmentTopic);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Invalid segment topic name")) {
        throw new IllegalArgumentException("segment:// names need '<parent-topic>/<hexStart>-<hexEnd>-<segmentId>'");
    }
    throw e;
}

Prevention

When it happens

Trigger: TopicName.get('segment://tenant/ns/topic') (no descriptor) or TopicName.get('segment://tenant/ns/0001-00ff-0') (descriptor but no parent-topic slash).

Common situations: Using the segment:// domain (scalable-segments feature) with an ordinary topic name; forgetting to append the segment descriptor; building the name by concatenation that drops the parent topic prefix.

Related errors


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