LMAX-Exchange/disruptor · error · IllegalArgumentException
value must be a positive number
Error message
value must be a positive number
What it means
IllegalArgumentException thrown by Util.log2 (src/main/java/com/lmax/disruptor/util/Util.java:101) when the supplied value is less than 1. Disruptor uses log2 to compute the bit shift for RingBuffer indexing, so any buffer size of 0 or negative reaches this guard. It indicates an invalid RingBuffer/Disruptor bufferSize argument rather than a runtime computation failure.
Source
Thrown at src/main/java/com/lmax/disruptor/util/Util.java:101
{
sequences[i] = processors[i].getSequence();
}
return sequences;
}
/**
* Calculate the log base 2 of the supplied integer, essentially reports the location
* of the highest bit.
*
* @param value Positive value to calculate log2 for.
* @return The log2 value
*/
public static int log2(final int value)
{
if (value < 1)
{
throw new IllegalArgumentException("value must be a positive number");
}
return Integer.SIZE - Integer.numberOfLeadingZeros(value) - 1;
}
/**
* @param mutex The object to wait on
* @param timeoutNanos The number of nanoseconds to wait for
* @return the number of nanoseconds waited (approximately)
* @throws InterruptedException if the underlying call to wait is interrupted
*/
public static long awaitNanos(final Object mutex, final long timeoutNanos) throws InterruptedException
{
long millis = timeoutNanos / ONE_MILLISECOND_IN_NANOSECONDS;
long nanos = timeoutNanos % ONE_MILLISECOND_IN_NANOSECONDS;
long t0 = System.nanoTime();
mutex.wait(millis, (int) nanos);
long t1 = System.nanoTime();View on GitHub (pinned to c871ca4982)
Solutions
- Set the ring buffer size to a positive power of two (minimum 1, typically 1024 or 4096): new Disruptor<>(factory, 1024, threadFactory).
- If the size comes from config, validate it at startup: require size >= 1 and Integer.bitCount(size) == 1 (power of two), failing fast with a clear config error.
- Fix arithmetic that computes the size: guard against int overflow (use long, check range) and clamp/round up to the next power of two with Integer.highestOneBit((size - 1) << 1) only after verifying size > 0.
- If 0 is legitimately possible (empty workload), short-circuit before constructing the Disruptor instead of passing 0 through.
Example fix
// before
int bufferSize = config.getQueueSize(); // may be 0 or negative from config
Disruptor<Event> disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory); // -> Util.log2 throws
// after
int requested = config.getQueueSize();
if (requested < 1) {
throw new IllegalArgumentException("queueSize must be >= 1, got: " + requested);
}
int bufferSize = Integer.highestOneBit(requested - 1) << 1; // round up to power of two
Disruptor<Event> disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory); Defensive patterns
Strategy: validation
Validate before calling
// Validate buffer size before constructing Disruptor/RingBuffer
static int requireValidBufferSize(final int requestedSize) {
if (requestedSize < 1) {
throw new IllegalArgumentException("bufferSize must be >= 1, got: " + requestedSize);
}
if (Integer.bitCount(requestedSize) != 1) {
throw new IllegalArgumentException("bufferSize must be a power of two, got: " + requestedSize);
}
return requestedSize;
} Try / catch
try {
disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory);
} catch (IllegalArgumentException e) {
if ("value must be a positive number".equals(e.getMessage())) {
throw new ConfigurationException("Invalid ring buffer size: " + bufferSize, e);
}
throw e;
} Prevention
- Treat ring buffer size as a validated configuration constant, not computed arithmetic; define named constants like RING_BUFFER_SIZE = 1024.
- Validate external config at startup (positive, power of two) rather than letting it surface deep inside Util.log2.
- When computing sizes, check for int overflow and round up with Integer.highestOneBit(x - 1) << 1 only after confirming x > 0.
When it happens
Trigger: Constructing new Disruptor<>(eventFactory, bufferSize, threadFactory) or RingBuffer.create(..) / new RingBuffer(..) with bufferSize <= 0 (e.g. 0, a negative constant, or an overflowed computed size such as (int)(items * factor) wrapping negative). Any intermediate value that resolves to < 1 before log2 is called triggers it.
Common situations: Buffer size read from configuration/environment that defaults to 0 when a property is missing; computing bufferSize from a formula that underflows or overflows (e.g. int overflow when multiplying up to a power of two); test fixtures creating tiny buffers with size 0; passing a byte/short value that was never set and defaults to 0.
Related errors
- bufferSize must not be less than 1
- bufferSize must be a power of 2
- maxBatchSize must be greater than 0
- n must be > 0 and < bufferSize
- Thread is already running
AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14).
Data as JSON: /api/errors/eb4ef788189ffbb1.
Report an issue: GitHub.