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
- Flush the batch by iterating minMax(v, ref) per element.
- Or point the batch flusher at an array-strategy entry.
- 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
- Async flush pipelines should resolve the strategy once and dispatch accordingly.
- Per-value minMax calls are cheap (CAS loops); no need to batch.
- Cover all strategies in flusher unit tests.
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
- minMax() is unavailable if countAndSum() has been called
- batchAdd() is unavailable if countAndSum() has been called
- count() is unavailable if minMax() has been called
- countAndSum() is unavailable if minMax() has been called
- arrayAdd() is unavailable if minMax() has been called
AI-assisted analysis of alibaba/Sentinel@a3f40ba8e9 (2026-08-14).
Data as JSON: /api/errors/c251c293595a85e5.
Report an issue: GitHub.