{"record":{"id":"fe25da4c3e576ee5","repo":"apache/skywalking","slug":"rank-rank-doesn-t-exist-in-the-previous-ranks-r","errorCode":null,"errorMessage":"Rank {rank} doesn't exist in the previous ranks {ranks}","messagePattern":"Rank (.+?) doesn't exist in the previous ranks (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java","lineNumber":140,"sourceCode":"                    \"Incompatible BucketedValues [\" + value + \"] for current PercentileFunction[\" + dataset + \"]\");\n            }\n        }\n\n        for (final int rank : value.getRanks()) {\n            if (rank <= 0) {\n                throw new IllegalArgumentException(\"Illegal rank value \" + rank + \", must be positive\");\n            }\n        }\n\n        if (ranks.size() > 0) {\n            if (ranks.size() != value.getRanks().length) {\n                throw new IllegalArgumentException(\n                    \"Incompatible ranks size = [\" + value.getRanks().length + \"] for current PercentileFunction[\" + ranks\n                        .size() + \"]\");\n            } else {\n                for (final int rank : value.getRanks()) {\n                    if (!ranks.include(rank)) {\n                        throw new IllegalArgumentException(\n                            \"Rank \" + rank + \" doesn't exist in the previous ranks \" + ranks);\n                    }\n                }\n            }\n        } else {\n            for (final int rank : value.getRanks()) {\n                ranks.add(rank);\n            }\n        }\n\n        this.entityId = entity.id();\n\n        String template = \"%s\";\n        if (CollectionUtils.isNotEmpty(value.getBucketedValues().getLabels())) {\n            template  = value.getBucketedValues().getLabels() + \":%s\";\n        }\n        final long[] values = value.getBucketedValues().getValues();\n        for (int i = 0; i < values.length; i++) {","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/apache/skywalking/blob/102af09b4a56064e22050dded10e2c52e490d040/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/function/avg/AvgHistogramPercentileFunction.java#L122-L158","documentation":"AvgHistogramPercentileFunction is the storage-backend-side aggregator for MAL `avg_histogram_percentile` metrics. On the first accept() in a time window it locks in the rank set (e.g. 50, 75, 90, 99); every later accept() for the same metric must pass the exact same ranks. This exception fires when a subsequent input carries a rank that was not in that first-locked set, i.e. the metric definition changed mid-window.","triggerScenarios":"Two MAL rules (or two OAP nodes, or a changed agent/meter config) emit the same avg_histogram_percentile metric name within the same minute window with different percentile rank arguments; the second input whose rank is absent from the locked IntList triggers the throw.","commonSituations":"Editing percentile ranks in meter-analyzer-config/*.yaml or otel-rules and rolling OAP instances one by one (old and new configs coexist); duplicating a metric name across MAL files with different ranks; a sending agent updated its rank config while the window is still open.","solutions":["Make every producer of this metric (all MAL rules, all OAP nodes in the cluster) use the identical rank list, e.g. percentile(50, 75, 90, 99) -> avg_histogram_percentile","If you intentionally changed ranks, wait for the current minute window to roll over (or restart all OAP nodes together) so the next window locks the new set","Grep all MAL/LAL-derived meter rules for the metric name to find the rule with divergent ranks"],"exampleFix":"# meter-analyzer-config/service.yaml — before (node A)\npercentile(50, 75, 90) -> avg_histogram_percentile\n# after (aligned everywhere)\npercentile(50, 75, 90, 99) -> avg_histogram_percentile","handlingStrategy":"validation","validationCode":"// before feeding a percentile metric, ensure the rank set matches the locked one\nIntList locked = function.getRanks(); // ranks locked this window\nint[] incoming = argument.getRanks();\nif (locked.size() > 0 && (locked.size() != incoming.length || Arrays.stream(incoming).anyMatch(r -> !locked.include(r)))) {\n    log.warn(\"Skipping incompatible rank set {} for metric {}; expected {}\", incoming, metricName, locked);\n    return; // do not call accept()\n}\nfunction.accept(entity, argument);","typeGuard":null,"tryCatchPattern":"catch (IllegalArgumentException e) only around the accept() call, log the metric name and both rank sets, drop the sample, and alert — never let one bad sample kill the meter pipeline thread.","preventionTips":["Treat the percentile rank list as an immutable metric contract; change it via coordinated OAP restart or a new metric name","Lint MAL configs in CI: assert every rule producing a given metric name uses identical percentile arguments","Use one shared YAML anchor/variable for rank lists so all rules stay in sync"],"tags":["meter-function","mal","percentile","metric-config","validation"],"backgroundTag":null,"analyzedSha":"102af09b4a56064e22050dded10e2c52e490d040","analyzedAt":"2026-08-14T10:47:52.647Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}