{"record":{"id":"a075a461ab39fb45","repo":"apache/pulsar","slug":"first-delta-must-be-0-got-deltas-get-0","errorCode":null,"errorMessage":"first delta must be > 0, got ${deltas.get(0)}","messagePattern":"first delta 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":94,"sourceCode":"     * <p>The {@code Stat} returned from the {@code put} carries the actual generated path. Pair\n     * with {@link MetadataStore#subscribeSequence} to receive notifications as new sequence keys\n     * are created.\n     *\n     * <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":76,"sourceCodeEnd":106,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-metadata/src/main/java/org/apache/pulsar/metadata/api/Option.java#L76-L106","documentation":"IllegalArgumentException thrown by the compact constructor of the SequenceKeysDeltas record in Option.java. The library enforces that a sequence-key delta list starts with a strictly positive first delta, because the first delta establishes the initial offset of the sequence; a zero or negative value would produce an invalid or ambiguous starting sequence.","triggerScenarios":"Constructing SequenceKeysDeltas (directly or via a metadata API that builds one) with a list whose first element is <= 0, e.g. List.of(0L, 5L) or a computed first delta of 0 when two consecutive keys are equal.","commonSituations":"Passing empty or degenerate delta lists computed from log/event sequences; off-by-one in delta computation producing 0 for the first entry; deserializing user-supplied delta arrays without pre-validation.","solutions":["Ensure the first element of the deltas list is strictly positive before constructing SequenceKeysDeltas","Fix the delta-computation logic so the first delta is derived as (firstKey - baseKey) with baseKey < firstKey","Pre-validate inputs with an explicit check and produce a domain-specific error instead of a raw IllegalArgumentException"],"exampleFix":"// before\nSequenceKeysDeltas d = new SequenceKeysDeltas(List.of(0L, 3L)); // throws\n// after\nList<Long> deltas = computeDeltas(keys);\nif (deltas.isEmpty() || deltas.get(0) <= 0) {\n    throw new IllegalStateException(\"first delta must be positive, got \" + (deltas.isEmpty() ? \"none\" : deltas.get(0)));\n}\nSequenceKeysDeltas d = new SequenceKeysDeltas(deltas);","handlingStrategy":"validation","validationCode":"if (deltas == null || deltas.isEmpty()) throw new IllegalArgumentException(\"deltas required\");\nif (deltas.get(0) <= 0) throw new IllegalArgumentException(\"first delta must be > 0\");\nfor (int i = 1; i < deltas.size(); i++) { if (deltas.get(i) < 0) throw new IllegalArgumentException(\"negative delta at \" + i); }","typeGuard":"static boolean isValidDeltas(List<Long> deltas) {\n    return deltas != null && !deltas.isEmpty() && deltas.get(0) > 0\n        && deltas.stream().skip(1).allMatch(d -> d >= 0);\n}","tryCatchPattern":null,"preventionTips":["Compute the first delta from a base strictly below the first key","Sort keys ascending before diffing","Validate delta lists at API boundaries before constructing the record"],"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-14T05:17:10.506Z"}