{"record":{"id":"eb4ef788189ffbb1","repo":"LMAX-Exchange/disruptor","slug":"value-must-be-a-positive-number","errorCode":null,"errorMessage":"value must be a positive number","messagePattern":"value must be a positive number","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/lmax/disruptor/util/Util.java","lineNumber":101,"sourceCode":"        {\n            sequences[i] = processors[i].getSequence();\n        }\n\n        return sequences;\n    }\n\n    /**\n     * Calculate the log base 2 of the supplied integer, essentially reports the location\n     * of the highest bit.\n     *\n     * @param value Positive value to calculate log2 for.\n     * @return The log2 value\n     */\n    public static int log2(final int value)\n    {\n        if (value < 1)\n        {\n            throw new IllegalArgumentException(\"value must be a positive number\");\n        }\n        return Integer.SIZE - Integer.numberOfLeadingZeros(value) - 1;\n    }\n\n    /**\n     * @param mutex The object to wait on\n     * @param timeoutNanos The number of nanoseconds to wait for\n     * @return the number of nanoseconds waited (approximately)\n     * @throws InterruptedException if the underlying call to wait is interrupted\n     */\n    public static long awaitNanos(final Object mutex, final long timeoutNanos) throws InterruptedException\n    {\n        long millis = timeoutNanos / ONE_MILLISECOND_IN_NANOSECONDS;\n        long nanos = timeoutNanos % ONE_MILLISECOND_IN_NANOSECONDS;\n\n        long t0 = System.nanoTime();\n        mutex.wait(millis, (int) nanos);\n        long t1 = System.nanoTime();","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/LMAX-Exchange/disruptor/blob/c871ca49826a6be7ada6957f6fbafcfecf7b1f87/src/main/java/com/lmax/disruptor/util/Util.java#L83-L119","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nint bufferSize = config.getQueueSize(); // may be 0 or negative from config\nDisruptor<Event> disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory); // -> Util.log2 throws\n\n// after\nint requested = config.getQueueSize();\nif (requested < 1) {\n    throw new IllegalArgumentException(\"queueSize must be >= 1, got: \" + requested);\n}\nint bufferSize = Integer.highestOneBit(requested - 1) << 1; // round up to power of two\nDisruptor<Event> disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory);","handlingStrategy":"validation","validationCode":"// Validate buffer size before constructing Disruptor/RingBuffer\nstatic int requireValidBufferSize(final int requestedSize) {\n    if (requestedSize < 1) {\n        throw new IllegalArgumentException(\"bufferSize must be >= 1, got: \" + requestedSize);\n    }\n    if (Integer.bitCount(requestedSize) != 1) {\n        throw new IllegalArgumentException(\"bufferSize must be a power of two, got: \" + requestedSize);\n    }\n    return requestedSize;\n}","typeGuard":null,"tryCatchPattern":"try {\n    disruptor = new Disruptor<>(Event::new, bufferSize, threadFactory);\n} catch (IllegalArgumentException e) {\n    if (\"value must be a positive number\".equals(e.getMessage())) {\n        throw new ConfigurationException(\"Invalid ring buffer size: \" + bufferSize, e);\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["java","disruptor","ring-buffer","validation","configuration"],"backgroundTag":null,"analyzedSha":"c871ca49826a6be7ada6957f6fbafcfecf7b1f87","analyzedAt":"2026-08-14T14:22:36.358Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}