{"record":{"id":"b444ea3f44c1a0c2","repo":"apache/pulsar","slug":"delta-at-index-i-must-be-0-got-deltas-get","errorCode":null,"errorMessage":"delta at index ${i} must be >= 0, got ${deltas.get(i)}","messagePattern":"delta at index (.+?) must be >= 0, got (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-metadata/src/main/java/org/apache/pulsar/metadata/api/Option.java","lineNumber":98,"sourceCode":"     * <p>Constraints: {@code deltas} must be non-empty, the first delta must be {@code > 0}, and\n     * the rest must be {@code >= 0}. On Oxia a {@link PartitionKey} must also be provided.\n     * Backends without native sequence-key support synthesize the same key format using a\n     * sidecar counter document and CAS.\n     *\n     * @param deltas per-dimension increments\n     */\n    record SequenceKeysDeltas(List<Long> deltas) implements Option {\n\n        public SequenceKeysDeltas {\n            if (deltas == null || deltas.isEmpty()) {\n                throw new IllegalArgumentException(\"SequenceKeysDeltas requires at least one delta\");\n            }\n            if (deltas.get(0) <= 0) {\n                throw new IllegalArgumentException(\"first delta must be > 0, got \" + deltas.get(0));\n            }\n            for (int i = 1; i < deltas.size(); i++) {\n                if (deltas.get(i) < 0) {\n                    throw new IllegalArgumentException(\n                            \"delta at index \" + i + \" must be >= 0, got \" + deltas.get(i));\n                }\n            }\n            deltas = List.copyOf(deltas);\n        }\n    }\n}\n","sourceCodeStart":80,"sourceCodeEnd":106,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/api/Option.java#L80-L106","documentation":"IllegalArgumentException thrown by the SequenceKeysDeltas compact constructor when any delta after index 0 is negative. Later deltas represent gaps between consecutive sequence keys and must be non-negative; a negative delta would mean sequence keys went backwards, which the library treats as a data-integrity violation.","triggerScenarios":"Constructing SequenceKeysDeltas with a list where deltas.get(i) < 0 for i >= 1, typically because keys were sorted in the wrong order (descending) or a diff was computed as earlier-minus-later key.","commonSituations":"Sorting keys descending instead of ascending before computing deltas; subtracting in the wrong direction; corrupted or reordered key streams.","solutions":["Sort the sequence keys in ascending order before computing deltas","Fix the delta computation to be (current - previous) with ascending order","Add a pre-check that rejects negative deltas with a clearer domain error"],"exampleFix":"// before\nList<Long> keys = keysFromSource; // may be unsorted\nSequenceKeysDeltas d = new SequenceKeysDeltas(toDeltas(keys)); // throws if a diff is negative\n// after\nList<Long> sorted = keys.stream().sorted().toList();\nList<Long> deltas = toDeltas(sorted); // each delta = cur - prev >= 0\nSequenceKeysDeltas d = new SequenceKeysDeltas(deltas);","handlingStrategy":"validation","validationCode":"for (int i = 1; i < deltas.size(); i++) {\n    if (deltas.get(i) < 0) throw new IllegalArgumentException(\"delta at \" + i + \" must be >= 0\");\n}","typeGuard":"static boolean hasNonNegativeDeltas(List<Long> deltas) {\n    return deltas == null || deltas.stream().skip(1).allMatch(d -> d >= 0);\n}","tryCatchPattern":null,"preventionTips":["Always sort sequence keys ascending before computing deltas","Use cur - previous consistently, never reverse the subtraction","Unit-test the delta builder with unsorted input"],"tags":["metadata","argument-validation","sequence-deltas"],"backgroundTag":"illegal-argument-validation","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}