{"record":{"id":"1ee376ab83205737","repo":"alibaba/Sentinel","slug":"count-is-unavailable-if-minmax-has-been-called","errorCode":null,"errorMessage":"count() is unavailable if minMax() has been called","messagePattern":"count\\(\\) is unavailable if minMax\\(\\) 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":154,"sourceCode":"            appender.append(lmin.ref);\n        }\n    }\n\n    @Override\n    public Object[] getValues() {\n        ValueRef lmax = max.get();\n        ValueRef lmin = min.get();\n        return new Object[] {lmax.value, lmax.ref, lmin.value, lmin.ref};\n    }\n\n    @Override\n    public int getStatType() {\n        return 4;\n    }\n\n    @Override\n    public void count(long count) {\n        throw new IllegalStateException(\"count() is unavailable if minMax() has been called\");\n    }\n\n    @Override\n    public void countAndSum(long count, long value) {\n        throw new IllegalStateException(\"countAndSum() is unavailable if minMax() has been called\");\n    }\n\n    @Override\n    public void arrayAdd(long... values) {\n        throw new IllegalStateException(\"arrayAdd() is unavailable if minMax() has been called\");\n    }\n\n    @Override\n    public void arraySet(long... values) {\n        throw new IllegalStateException(\"arraySet() is unavailable if minMax() has been called\");\n    }\n\n    @Override","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/alibaba/Sentinel/blob/a3f40ba8e900c8489bd520274739f17235a7721c/sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/StatEntryFunc.java#L136-L172","documentation":"StatEntryFuncMinMax implements the min/max strategy (stat type 4): it keeps only two ValueRef slots — running max and running min with reference strings — maintained via lock-free CAS loops. It has no occurrence counter, so count(long) is unsupported and throws IllegalStateException.","triggerScenarios":"Calling count(n) on an entry created with the minMax strategy — e.g. a throughput counter call left in place after the metric was converted to min/max tracking, or a generic recorder that always counts and additionally records extremes.","commonSituations":"Metric migrated from counter to latency min/max without removing old count() call sites. Shared recording helper hard-codes count() plus optional extras. Config-driven type change missed in one module.","solutions":["Remove the count() call for min/max entries; min/max entries track extremes only.","If occurrence counts are also needed, maintain a separate count-and-sum entry in the same logger.","In generic recorders, branch on getStatType() == 4 to skip count() for min/max entries."],"exampleFix":"// before\nfunc.count(1); // func is StatEntryFuncMinMax -> throws\nfunc.minMax(value, ref);\n\n// after\nfunc.minMax(value, ref);\n// counts go to a separate count-and-sum entry\ncountFunc.countAndSum(1, value);","handlingStrategy":"type-guard","validationCode":"if (func.getStatType() != 4) { func.count(1); }\nfunc.minMax(value, ref); // always ok on min/max","typeGuard":"boolean isMinMax(StatEntryFunc f) { return f.getStatType() == 4; }","tryCatchPattern":null,"preventionTips":["When converting a counter metric to min/max, grep for all count() call sites of that entry.","Keep counts in a dedicated count-and-sum entry.","Branch shared recorders on getStatType() == 4."],"tags":["eagleeye","stat-func","api-misuse","illegal-state","min-max","sentinel"],"backgroundTag":null,"analyzedSha":"a3f40ba8e900c8489bd520274739f17235a7721c","analyzedAt":"2026-08-14T11:10:30.678Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}