apache/pulsar · error · IllegalArgumentException

Ranges for KeyShared policy with overlap between

Error message

Ranges for KeyShared policy with overlap between 

What it means

KeySharedPolicy.KeySharedPolicySticky.validate() performs an O(n^2) pairwise check that no two sticky hash ranges intersect. If range1.intersect(range2) returns non-null for two distinct ranges, the same hash slots would be claimed by multiple consumers, so key-to-consumer assignment would be ambiguous. The policy is rejected with IllegalArgumentException naming both overlapping ranges.

Source

Thrown at pulsar-client-api/src/main/java/org/apache/pulsar/client/api/KeySharedPolicy.java:119

        public KeySharedPolicySticky ranges(Range... ranges) {
            this.ranges.addAll(Arrays.asList(ranges));
            return this;
        }

        @Override
        public void validate() {
            if (ranges.isEmpty()) {
                throw new IllegalArgumentException("Ranges for KeyShared policy must not be empty.");
            }
            for (int i = 0; i < ranges.size(); i++) {
                Range range1 = ranges.get(i);
                if (range1.getStart() < 0 || range1.getEnd() >= DEFAULT_HASH_RANGE_SIZE) {
                    throw new IllegalArgumentException("Ranges must be [0, 65535] but provided range is " + range1);
                }
                for (int j = 0; j < ranges.size(); j++) {
                    Range range2 = ranges.get(j);
                    if (i != j && range1.intersect(range2) != null) {
                        throw new IllegalArgumentException("Ranges for KeyShared policy with overlap between " + range1
                                + " and " + range2);
                    }
                }
            }
        }

        public List<Range> getRanges() {
            return ranges;
        }
    }

    /**
     * Auto split hash range key shared policy.
     */
    public static class KeySharedPolicyAutoSplit extends KeySharedPolicy {
        private static final long serialVersionUID = 1L;

        KeySharedPolicyAutoSplit() {

View on GitHub (pinned to 820761864e)

Solutions

  1. Adjust the ranges so every slot belongs to at most one range, e.g. Range.of(0, 100) and Range.of(101, 200).
  2. Generate ranges programmatically by partitioning 0..65534 into N equal non-overlapping slices instead of hardcoding boundaries.
  3. Sort ranges by start and assert range[i].end < range[i+1].start before calling stickyRanges.

Example fix

// before
List<Range> ranges = Arrays.asList(Range.of(0, 100), Range.of(50, 200));
// after
List<Range> ranges = Arrays.asList(Range.of(0, 100), Range.of(101, 200)); // no overlap
Defensive patterns

Strategy: validation

Validate before calling

static void checkNoOverlap(List<Range> ranges) {
    List<Range> sorted = new ArrayList<>(ranges);
    sorted.sort(Comparator.comparingInt(Range::getStart));
    for (int i = 1; i < sorted.size(); i++) {
        if (sorted.get(i).getStart() <= sorted.get(i - 1).getEnd())
            throw new IllegalArgumentException("Overlap: " + sorted.get(i - 1) + " vs " + sorted.get(i));
    }
}
checkNoOverlap(ranges); // before stickyRanges

Type guard

static boolean isNonOverlapping(List<Range> ranges) {
    for (int i = 0; i < ranges.size(); i++)
        for (int j = i + 1; j < ranges.size(); j++)
            if (ranges.get(i).intersect(ranges.get(j)) != null) return false;
    return true;
}

Try / catch

try {
    KeySharedPolicy policy = KeySharedPolicy.stickyRanges(ranges);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Ranges for KeyShared policy with overlap")) {
        // parse the two ranges from the message, split the shared boundary
    } else throw e;
}

Prevention

When it happens

Trigger: Building KeySharedPolicy.stickyRanges(Arrays.asList(Range.of(0, 100), Range.of(50, 200))) — any two ranges whose [start, end] intervals share at least one slot (e.g. touching via a common index) when the consumer attaches.

Common situations: Programmatically splitting the hash space with rounding errors (ceil/floor producing duplicate boundary values); copy-pasting range lists across consumers and forgetting to shrink one; merging configs where two tenants were assigned overlapping slices.

Related errors


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