apache/pulsar · error · IllegalArgumentException

delta at index ${i} must be >= 0, got ${deltas.get(i)}

Error message

delta at index ${i} must be >= 0, got ${deltas.get(i)}

What it means

IllegalArgumentException thrown by the SequenceKeysDeltas compact constructor when any delta after index 0 is negative. Later deltas represent gaps between consecutive sequence keys and must be non-negative; a negative delta would mean sequence keys went backwards, which the library treats as a data-integrity violation.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/api/Option.java:98

     * <p>Constraints: {@code deltas} must be non-empty, the first delta must be {@code > 0}, and
     * the rest must be {@code >= 0}. On Oxia a {@link PartitionKey} must also be provided.
     * Backends without native sequence-key support synthesize the same key format using a
     * sidecar counter document and CAS.
     *
     * @param deltas per-dimension increments
     */
    record SequenceKeysDeltas(List<Long> deltas) implements Option {

        public SequenceKeysDeltas {
            if (deltas == null || deltas.isEmpty()) {
                throw new IllegalArgumentException("SequenceKeysDeltas requires at least one delta");
            }
            if (deltas.get(0) <= 0) {
                throw new IllegalArgumentException("first delta must be > 0, got " + deltas.get(0));
            }
            for (int i = 1; i < deltas.size(); i++) {
                if (deltas.get(i) < 0) {
                    throw new IllegalArgumentException(
                            "delta at index " + i + " must be >= 0, got " + deltas.get(i));
                }
            }
            deltas = List.copyOf(deltas);
        }
    }
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Sort the sequence keys in ascending order before computing deltas
  2. Fix the delta computation to be (current - previous) with ascending order
  3. Add a pre-check that rejects negative deltas with a clearer domain error

Example fix

// before
List<Long> keys = keysFromSource; // may be unsorted
SequenceKeysDeltas d = new SequenceKeysDeltas(toDeltas(keys)); // throws if a diff is negative
// after
List<Long> sorted = keys.stream().sorted().toList();
List<Long> deltas = toDeltas(sorted); // each delta = cur - prev >= 0
SequenceKeysDeltas d = new SequenceKeysDeltas(deltas);
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 1; i < deltas.size(); i++) {
    if (deltas.get(i) < 0) throw new IllegalArgumentException("delta at " + i + " must be >= 0");
}

Type guard

static boolean hasNonNegativeDeltas(List<Long> deltas) {
    return deltas == null || deltas.stream().skip(1).allMatch(d -> d >= 0);
}

Prevention

When it happens

Trigger: Constructing SequenceKeysDeltas with a list where deltas.get(i) < 0 for i >= 1, typically because keys were sorted in the wrong order (descending) or a diff was computed as earlier-minus-later key.

Common situations: Sorting keys descending instead of ascending before computing deltas; subtracting in the wrong direction; corrupted or reordered key streams.

Related errors


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