apache/pulsar · error · IllegalArgumentException

SequenceKeysDeltas requires at least one delta

Error message

SequenceKeysDeltas requires at least one delta

What it means

Option.SequenceKeysDeltas is a record representing per-dimension increments for a sequence subscription; its compact constructor validates the input. It throws IllegalArgumentException('SequenceKeysDeltas requires at least one delta') when constructed with a null or empty delta list, since a sequence advance must move at least the first dimension.

Source

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

     * {@code prefix-{seq0}-{seq1}-...} where each sequence is zero-padded 20-digit decimal and
     * each dimension increments atomically by its delta.
     *
     * <p>The {@code Stat} returned from the {@code put} carries the actual generated path. Pair
     * with {@link MetadataStore#subscribeSequence} to receive notifications as new sequence keys
     * are created.
     *
     * <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. Ensure the delta list has at least one element before constructing, e.g. guard with List.isEmpty().
  2. Provide a default first delta (>= 1) when no dimension state is available.
  3. Fix upstream logic that produces empty delta lists so callers never construct the option with no deltas.

Example fix

// before
Option opt = new SequenceKeysDeltas(computedDeltas); // may be empty
// after
if (computedDeltas == null || computedDeltas.isEmpty()) {
    computedDeltas = List.of(1L); // minimal valid advance
}
Option opt = new SequenceKeysDeltas(computedDeltas);
Defensive patterns

Strategy: validation

Validate before calling

if (deltas == null || deltas.isEmpty()) {
    throw new IllegalArgumentException("SequenceKeysDeltas needs at least one delta");
}
Option opt = new SequenceKeysDeltas(deltas);

Type guard

boolean validDeltas(List<Long> deltas) {
    return deltas != null && !deltas.isEmpty() && deltas.get(0) > 0
        && deltas.stream().skip(1).allMatch(d -> d >= 0);
}

Try / catch

try {
    Option opt = new SequenceKeysDeltas(deltas);
} catch (IllegalArgumentException e) {
    log.warn("Invalid deltas: {}", e.getMessage());
    opt = new SequenceKeysDeltas(List.of(1L));
}

Prevention

When it happens

Trigger: new SequenceKeysDeltas(List.of()) or new SequenceKeysDeltas(null), or programmatically building deltas from an empty collection before passing them to subscribeSequence.

Common situations: Code computing deltas from variable-dimension state that produced zero entries; deserializing a malformed/empty options payload; copy-paste building an empty list placeholder.

Related errors


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