apache/pulsar · error · IllegalStateException
No segment covers hash + hash + for key: + key
Error message
No segment covers hash + hash + for key: + key
What it means
SegmentRouter.route() throws IllegalStateException when no active segment's hashRange contains the computed hash of the key, i.e. the hash ring has a coverage gap. The exception message includes the numeric hash and the original key for diagnosis.
Source
Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/SegmentRouter.java:66
* @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()) {
throw new IllegalStateException("No active segments");
}
int idx = Math.abs(roundRobinCounter.getAndIncrement() % activeSegments.size());
return activeSegments.get(idx).segmentId();
}
/** True iff every active segment is a legacy segment — signals a synthetic-layout topic. */
private static boolean allLegacy(List<ActiveSegment> activeSegments) {
for (var s : activeSegments) {
if (!s.isLegacy()) {
return false;View on GitHub (pinned to 820761864e)
Solutions
- Refresh the segment layout (wait for the next DAG watch update) — the gap is usually transient.
- Check hash-range construction: ranges must fully cover the hash space with no gaps after split/merge.
- If persistent, validate layout integrity before use (assert contiguous full coverage) and reject invalid layouts.
- Report the key/hash from the message — a bug in hashRange.contains or hash() may need a library fix.
Example fix
// before long seg = router.route(key, segments); // gap -> IllegalStateException // after validateCoverage(segments); // assert union of hashRanges covers full space long seg = router.route(key, segments);
Defensive patterns
Strategy: validation
Validate before calling
// ensure hash ranges fully cover the hash space before routing
int covered = 0;
for (var s : segments) { covered |= s.hashRange().coverageMask(); } // domain-specific check
// or simply refresh the layout if a gap is detected Type guard
static boolean layoutCoversAll(List<SegmentRouter.ActiveSegment> s) {
// verify union of hashRanges has no gaps after sorting by range start
return s.stream().map(SegmentRouter.ActiveSegment::hashRange)
.sorted(Comparator.comparing(HashRange::start))
.reduce((a, b) -> b.start() <= a.end() + 1 ? b : null).isPresent(); // simplified
} Try / catch
try {
long seg = router.route(key, activeSegments);
} catch (IllegalStateException e) {
refreshLayout(); // transient gap: wait for next DAG watch update
} Prevention
- Validate that split/merge layouts remain contiguous with no coverage gaps
- Refresh the layout when a coverage gap is detected — gaps are usually transient
- Keep clients on matching library versions so hash-range construction agrees
When it happens
Trigger: route(key, activeSegments) where activeSegments is non-empty but none of their hashRanges contains hash(key) — typically an inconsistent or partially delivered layout after a split/merge.
Common situations: Mid-transition layouts where a split has sealed the parent segment before child segments are active; mixed-version clients building hash ranges; manual layout construction missing a range.
Related errors
- No active segments
- Synthetic layout missing segment_id= + partition + (N= + n
- Segments are not adjacent: ${hashRange1} and ${hashRange2}
- No more range can assigned to new consumer, assigned consume
- Range conflict with consumer ${conflictingConsumer}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/dd3049ce0a6a5483.
Report an issue: GitHub.