alibaba/Sentinel · error · IllegalStateException

batchAdd() is unavailable if countAndSum() has been called

Error message

batchAdd() is unavailable if countAndSum() has been called

What it means

StatEntryFuncCountAndSum only supports count() and countAndSum(); batchAdd(long... values), which adds multiple values into pre-allocated array slots in one call, is incompatible with its two-scalar (count, sum) state and therefore throws IllegalStateException.

Solutions

  1. Flush batched values by looping countAndSum(1, v) over the batch instead of batchAdd.
  2. Or recreate the entry with an array-type func that supports batchAdd.
  3. Validate the metric-type/method pairing at startup with a smoke test that writes one record.

Example fix

// before
func.batchAdd(values); // count-and-sum entry -> throws

// after
for (long v : values) { func.countAndSum(1, v); }
Defensive patterns

Strategy: type-guard

Validate before calling

switch (func.getStatType()) {
    case 2: for (long v : values) func.countAndSum(1, v); break;
    default: func.batchAdd(values);
}

Type guard

boolean isCountAndSum(StatEntryFunc f) { return f.getStatType() == 2; }

Prevention

When it happens

Trigger: Invoking batchAdd(values) on an entry whose func was created for count-and-sum — commonly a batching layer that groups several measurements and flushes them with batchAdd, pointed at a countAndSum entry by mistake.

Common situations: A batch-flushing recorder is reused across metric types. Metric type changed in config from array/batch to count_and_sum without updating the flusher. Copy-paste of a batch example into a counter metric.

Related errors


AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14). Data as JSON: /api/errors/e7e4c241fe3a61a4. Report an issue: GitHub.

Appendix: source

Thrown at sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/StatEntryFunc.java:109

    @Override
    public void arrayAdd(long... values) {
        throw new IllegalStateException("arrayAdd() is unavailable if countAndSum() has been called");
    }

    @Override
    public void arraySet(long... values) {
        throw new IllegalStateException("arraySet() is unavailable if countAndSum() has been called");
    }

    @Override
    public void minMax(long candidate, String ref) {
        throw new IllegalStateException("minMax() is unavailable if countAndSum() has been called");
    }

    @Override
    public void batchAdd(long... values) {
        throw new IllegalStateException("batchAdd() is unavailable if countAndSum() has been called");
    }

    @Override
    public void strArray(String... values) {
        throw new IllegalStateException("strArray() is unavailable if countAndSum() has been called");
    }
}

class StatEntryFuncMinMax implements StatEntryFunc {

    private AtomicReference<ValueRef> max = new AtomicReference<ValueRef>(new ValueRef(Long.MIN_VALUE, null));
    private AtomicReference<ValueRef> min = new AtomicReference<ValueRef>(new ValueRef(Long.MAX_VALUE, null));

    @Override
    public void appendTo(StringBuilder appender, char delimiter) {
        ValueRef lmax = max.get();
        ValueRef lmin = min.get();

View on GitHub (pinned to a3f40ba8e9)