apache/pulsar · error · IllegalStateException
No active segments
Error message
No active segments
What it means
SegmentRouter.route() throws IllegalStateException when the supplied activeSegments list is empty. The router cannot pick a destination segment without at least one active segment, so routing a keyed message fails fast.
Source
Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/SegmentRouter.java:55
private final AtomicInteger roundRobinCounter = new AtomicInteger(0);
/**
* Route a message key to the segment that owns its hash range.
*
* <p>If every active segment is a legacy segment (synthetic layout for a not-yet-migrated
* regular topic), the routing switches to {@code signSafeMod(murmurHash3_32(key), N)} over
* {@code segment_id} so V5 producers route the same way v4 partitioned-topic producers do
* — preserving per-key destinations while clients gradually upgrade.
*
* @param key the message key
* @param activeSegments the currently active segments (sorted by hash range)
* @return the segment ID to route to
* @throws IllegalStateException if no segment covers the hash
*/
long route(String key, List<ActiveSegment> activeSegments) {
if (activeSegments.isEmpty()) {
throw new IllegalStateException("No active segments");
}
if (allLegacy(activeSegments)) {
return routeModN(key, activeSegments);
}
int hash = hash(key);
for (var segment : activeSegments) {
if (segment.hashRange().contains(hash)) {
return segment.segmentId();
}
}
throw new IllegalStateException("No segment covers hash " + hash + " for key: " + key);
}
/**
* Route a message without a key using round-robin across active segments.
*/
long routeRoundRobin(List<ActiveSegment> activeSegments) {
if (activeSegments.isEmpty()) {View on GitHub (pinned to 820761864e)
Solutions
- Wait for the initial layout/DAG watch callback before routing; use eagerAttachInitialAsync to pre-populate segments.
- Guard the call: check activeSegments non-empty (or fall back to buffering/rejecting sends) before invoking route.
- If all segments are sealed, check topic state — the topic may be terminated and should not accept sends.
- Fix caller logic that empties the list via filters or stale snapshot handling.
Example fix
// before
long seg = router.route(key, activeSegments); // throws when empty
// after
if (activeSegments.isEmpty()) { throw new NoLayoutException("layout not ready"); }
long seg = router.route(key, activeSegments); Defensive patterns
Strategy: validation
Validate before calling
if (activeSegments == null || activeSegments.isEmpty()) {
throw new IllegalStateException("Layout not ready: no active segments to route key");
}
long seg = router.route(key, activeSegments); Type guard
static boolean hasActiveSegments(List<SegmentRouter.ActiveSegment> s) {
return s != null && !s.isEmpty();
} Try / catch
try {
long seg = router.route(key, activeSegments);
} catch (IllegalStateException e) {
// layout not loaded yet: buffer or defer the message
} Prevention
- Wait for the first DAG watch/layout callback before routing
- Use eagerAttachInitialAsync to pre-populate active segments
- Check topic termination state before producing
When it happens
Trigger: Calling route(key, activeSegments) (directly or through producer routing) with an empty list — e.g. layout not yet loaded, all segments sealed, or a caller passing a filtered/empty list of ActiveSegment.
Common situations: Producing immediately after topic creation before the first layout arrives; topic terminated or migrated leaving no active segments; bugs in caller code filtering the segment list to empty.
Related errors
- No segment covers hash + hash + for key: + key
- Synthetic layout missing segment_id= + partition + (N= + n
- ServiceUrlProvider has already been initialized
- Interrupted while waiting for layout update
- Error creating client for HealthChecker
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/20c0193b5bcf657d.
Report an issue: GitHub.