{"record":{"id":"e7e4c241fe3a61a4","repo":"alibaba/Sentinel","slug":"batchadd-is-unavailable-if-countandsum-has-bee","errorCode":null,"errorMessage":"batchAdd() is unavailable if countAndSum() has been called","messagePattern":"batchAdd\\(\\) is unavailable if countAndSum\\(\\) has been called","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/StatEntryFunc.java","lineNumber":109,"sourceCode":"\n    @Override\n    public void arrayAdd(long... values) {\n        throw new IllegalStateException(\"arrayAdd() is unavailable if countAndSum() has been called\");\n    }\n\n    @Override\n    public void arraySet(long... values) {\n        throw new IllegalStateException(\"arraySet() is unavailable if countAndSum() has been called\");\n    }\n\n    @Override\n    public void minMax(long candidate, String ref) {\n        throw new IllegalStateException(\"minMax() is unavailable if countAndSum() has been called\");\n    }\n\n    @Override\n    public void batchAdd(long... values) {\n        throw new IllegalStateException(\"batchAdd() is unavailable if countAndSum() has been called\");\n    }\n\n    @Override\n    public void strArray(String... values) {\n        throw new IllegalStateException(\"strArray() is unavailable if countAndSum() has been called\");\n    }\n}\n\nclass StatEntryFuncMinMax implements StatEntryFunc {\n\n    private AtomicReference<ValueRef> max = new AtomicReference<ValueRef>(new ValueRef(Long.MIN_VALUE, null));\n    private AtomicReference<ValueRef> min = new AtomicReference<ValueRef>(new ValueRef(Long.MAX_VALUE, null));\n\n    @Override\n    public void appendTo(StringBuilder appender, char delimiter) {\n        ValueRef lmax = max.get();\n        ValueRef lmin = min.get();\n","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/alibaba/Sentinel/blob/a3f40ba8e900c8489bd520274739f17235a7721c/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/StatEntryFunc.java#L91-L127","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Flush batched values by looping countAndSum(1, v) over the batch instead of batchAdd.","Or recreate the entry with an array-type func that supports batchAdd.","Validate the metric-type/method pairing at startup with a smoke test that writes one record."],"exampleFix":"// before\nfunc.batchAdd(values); // count-and-sum entry -> throws\n\n// after\nfor (long v : values) { func.countAndSum(1, v); }","handlingStrategy":"type-guard","validationCode":"switch (func.getStatType()) {\n    case 2: for (long v : values) func.countAndSum(1, v); break;\n    default: func.batchAdd(values);\n}","typeGuard":"boolean isCountAndSum(StatEntryFunc f) { return f.getStatType() == 2; }","tryCatchPattern":null,"preventionTips":["Batch flushers should query the strategy before choosing the flush method.","Prefer per-value writes (countAndSum loop) for count/sum metrics; batching gains little there.","Test the flush path with each metric type in CI."],"tags":["eagleeye","stat-func","api-misuse","illegal-state","batch","sentinel"],"backgroundTag":null,"analyzedSha":"a3f40ba8e900c8489bd520274739f17235a7721c","analyzedAt":"2026-08-14T11:10:30.678Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}