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

  1. Set the ring buffer size to a positive power of two (minimum 1, typically 1024 or 4096): new Disruptor<>(factory, 1024, threadFactory).
  2. 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.
  3. 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.
  4. 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

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


AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14). Data as JSON: /api/errors/eb4ef788189ffbb1. Report an issue: GitHub.