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

  1. Refresh the segment layout (wait for the next DAG watch update) — the gap is usually transient.
  2. Check hash-range construction: ranges must fully cover the hash space with no gaps after split/merge.
  3. If persistent, validate layout integrity before use (assert contiguous full coverage) and reject invalid layouts.
  4. 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

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


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