alibaba/Sentinel · error · IllegalStateException

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

Error message

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

What it means

StatEntryFuncMinMax throws IllegalStateException from batchAdd(long... values). Batch-add pushes several values into array slots at once; the min/max strategy instead folds every single observation into its two CAS-guarded ValueRef slots via the minMax method, so a batch API has no meaningful implementation here.

Source

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

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

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

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

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

    @Override
    public void minMax(long candidate, String ref) {
        ValueRef lmax = max.get();
        if (lmax.value <= candidate) {
            final ValueRef cmax = new ValueRef(candidate, ref);
            while (!max.compareAndSet(lmax, cmax) && (lmax = max.get()).value <= candidate) { ; }
        }
        ValueRef lmin = min.get();
        if (lmin.value >= candidate) {
            final ValueRef cmin = new ValueRef(candidate, ref);
            while (!min.compareAndSet(lmin, cmin) && (lmin = min.get()).value >= candidate) { ; }
        }
    }

    @Override
    public void strArray(String... values) {

View on GitHub (pinned to a3f40ba8e9)

Solutions

  1. Flush the batch by iterating minMax(v, ref) per element.
  2. Or point the batch flusher at an array-strategy entry.
  3. Centralize the type/method dispatch so batch flushers query getStatType() first.

Example fix

// before
func.batchAdd(buffered); // minMax func -> throws

// after
for (LatencySample s : buffered) { func.minMax(s.value, s.ref); }
Defensive patterns

Strategy: type-guard

Validate before calling

if (func.getStatType() == 4) {
    for (Sample s : batch) func.minMax(s.value, s.ref);
} else {
    func.batchAdd(values);
}

Type guard

boolean isMinMax(StatEntryFunc f) { return f.getStatType() == 4; }

Prevention

When it happens

Trigger: A batching flusher calls batchAdd(buffer) on an entry that was created with the minMax strategy — e.g. buffered latency measurements flushed periodically to a min/max metric.

Common situations: Buffered async metric pipelines shared across metric types. Config-driven metric type switched to min/max while the flusher kept batchAdd. Code copied from an array metric module.

Related errors


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