{"record":{"id":"ce8662846a1db732","repo":"LMAX-Exchange/disruptor","slug":"n-must-be-0-and-buffersize","errorCode":null,"errorMessage":"n must be > 0 and < bufferSize","messagePattern":"n must be > 0 and < bufferSize","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/lmax/disruptor/MultiProducerSequencer.java","lineNumber":116,"sourceCode":"\n    /**\n     * @see Sequencer#next()\n     */\n    @Override\n    public long next()\n    {\n        return next(1);\n    }\n\n    /**\n     * @see Sequencer#next(int)\n     */\n    @Override\n    public long next(final int n)\n    {\n        if (n < 1 || n > bufferSize)\n        {\n            throw new IllegalArgumentException(\"n must be > 0 and < bufferSize\");\n        }\n\n        long current = cursor.getAndAdd(n);\n\n        long nextSequence = current + n;\n        long wrapPoint = nextSequence - bufferSize;\n        long cachedGatingSequence = gatingSequenceCache.get();\n\n        if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)\n        {\n            long gatingSequence;\n            while (wrapPoint > (gatingSequence = Util.getMinimumSequence(gatingSequences, current)))\n            {\n                LockSupport.parkNanos(1L); // TODO, should we spin based on the wait strategy?\n            }\n\n            gatingSequenceCache.set(gatingSequence);\n        }","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/LMAX-Exchange/disruptor/blob/c871ca49826a6be7ada6957f6fbafcfecf7b1f87/src/main/java/com/lmax/disruptor/MultiProducerSequencer.java#L98-L134","documentation":"Thrown by MultiProducerSequencer.next(int n) when n < 1 or n > bufferSize. next(n) claims n sequential slots in one call; claiming zero or negative slots is meaningless, and claiming more than the whole ring at once would deadlock because publishers can never wrap past themselves.","triggerScenarios":"Calling ringBuffer.next(0), ringBuffer.next(n) with n negative, or next(bufferSize + 1) — commonly next(someList.size()) where the list is empty or larger than the ring; also publishing batches via publish(start, size) with a bad size internally routing through next(n).","commonSituations":"Batch-publishing code that does next(items.size()) without checking for an empty list; a batch size configured larger than the ring buffer capacity (e.g. 8192-item batches into a 4096 ring); a size field that underflows to negative.","solutions":["Guard the caller: skip publishing when the batch is empty, and clamp/split batches so n <= bufferSize.","If batches can exceed the ring, raise bufferSize to at least the max batch, or chunk the batch into multiple next/publish cycles of at most bufferSize.","Add an assert/log for computed batch sizes to catch underflow early."],"exampleFix":"// before\nlong hi = ringBuffer.next(items.size()); // items may be empty or > bufferSize\n\n// after\nif (items.isEmpty()) return;\nint n = Math.min(items.size(), ringBuffer.getBufferSize());\nlong hi = ringBuffer.next(n);","handlingStrategy":"validation","validationCode":"if (n < 1 || n > ringBuffer.getBufferSize()) {\n    throw new IllegalArgumentException(\"batch claim n must be in [1, \" + ringBuffer.getBufferSize() + \"]\");\n}\nlong hi = ringBuffer.next(n);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guard every next(items.size()) with an isEmpty() check.","Keep max batch size <= ring buffer size as an enforced invariant at startup."],"tags":["disruptor","producer","batching","ring-buffer","validation"],"backgroundTag":null,"analyzedSha":"c871ca49826a6be7ada6957f6fbafcfecf7b1f87","analyzedAt":"2026-08-14T14:22:36.358Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}