LMAX-Exchange/disruptor · error · IllegalArgumentException
Both batchStartsAt and batchSize must be positive but got: b
Error message
Both batchStartsAt and batchSize must be positive but got: batchStartsAt {} and batchSize {} What it means
Thrown by RingBuffer.checkBatchSizing during batch-publish overloads (publishEvents, tryPublishEvents) when batchStartsAt is negative or batchSize is negative. batchStartsAt is the offset into your argument arrays where the batch begins; both it and the count must be non-negative for the copy into the ring to be well-defined.
Source
Thrown at src/main/java/com/lmax/disruptor/RingBuffer.java:904
* @return The number of slots remaining.
*/
@Override
public long remainingCapacity()
{
return sequencer.remainingCapacity();
}
private void checkBounds(final EventTranslator<E>[] translators, final int batchStartsAt, final int batchSize)
{
checkBatchSizing(batchStartsAt, batchSize);
batchOverRuns(translators, batchStartsAt, batchSize);
}
private void checkBatchSizing(final int batchStartsAt, final int batchSize)
{
if (batchStartsAt < 0 || batchSize < 0)
{
throw new IllegalArgumentException("Both batchStartsAt and batchSize must be positive but got: batchStartsAt " + batchStartsAt + " and batchSize " + batchSize);
}
else if (batchSize > bufferSize)
{
throw new IllegalArgumentException("The ring buffer cannot accommodate " + batchSize + " it only has space for " + bufferSize + " entities.");
}
}
private <A> void checkBounds(final A[] arg0, final int batchStartsAt, final int batchSize)
{
checkBatchSizing(batchStartsAt, batchSize);
batchOverRuns(arg0, batchStartsAt, batchSize);
}
private <A, B> void checkBounds(final A[] arg0, final B[] arg1, final int batchStartsAt, final int batchSize)
{
checkBatchSizing(batchStartsAt, batchSize);
batchOverRuns(arg0, batchStartsAt, batchSize);
batchOverRuns(arg1, batchStartsAt, batchSize);View on GitHub (pinned to c871ca4982)
Solutions
- Fix the offset/count math: batchStartsAt must be >= 0 and batchSize >= 1.
- To publish from the beginning of the array, pass 0 explicitly rather than -1.
- Log batchStartsAt/batchSize before publishing while debugging boundary loops.
Example fix
// before ringBuffer.publishEvents(translators, offset - translators.length, count); // negative offset // after int start = Math.max(0, offset); ringBuffer.publishEvents(translators, start, count);
Defensive patterns
Strategy: validation
Validate before calling
if (batchStartsAt < 0 || batchSize < 1 || batchStartsAt >= translators.length) {
throw new IllegalArgumentException("bad batch bounds: start=" + batchStartsAt + " size=" + batchSize);
} Prevention
- Use 0 as the explicit start offset; never pass sentinels like -1.
- Compute offsets in one place and unit-test boundary values.
When it happens
Trigger: Calling ringBuffer.publishEvents(translators, -1, 2) or publishEvents(translators, 0, -2) — i.e. passing a negative offset or count into any batch publish variant (translators, one-arg, two-arg, three-arg, varargs forms).
Common situations: Offset arithmetic bugs (batchStartsAt = lastEnd - batchLength going negative at a boundary); loop code computing 'start = i - batchSize' incorrectly; passing a sentinel like -1 to mean 'from the start'.
Related errors
- n must be > 0 and < bufferSize
- The ring buffer cannot accommodate {} it only has space for
- A batchSize of: {} with batchStatsAt of: {} will overrun the
- bufferSize must not be less than 1
- bufferSize must be a power of 2
AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14).
Data as JSON: /api/errors/a4ad639e14e7df18.
Report an issue: GitHub.