LMAX-Exchange/disruptor · error · IllegalStateException
Thread is already running
Error message
Thread is already running
What it means
Defensive check in RingBufferFields' constructor: bufferSize taken from the sequencer is less than 1. In practice this is nearly unreachable because AbstractSequencer already validated the size (error 0); it exists to protect against a custom Sequencer implementation whose getBufferSize() returns garbage.
Source
Thrown at src/main/java/com/lmax/disruptor/NoOpEventProcessor.java:63
@Override
public void halt()
{
running.set(false);
}
@Override
public boolean isRunning()
{
return running.get();
}
@Override
public void run()
{
if (!running.compareAndSet(false, true))
{
throw new IllegalStateException("Thread is already running");
}
}
/**
* Sequence that follows (by wrapping) another sequence
*/
private static final class SequencerFollowingSequence extends Sequence
{
private final RingBuffer<?> sequencer;
private SequencerFollowingSequence(final RingBuffer<?> sequencer)
{
super(Sequencer.INITIAL_CURSOR_VALUE);
this.sequencer = sequencer;
}
@Override
public long get()View on GitHub (pinned to c871ca4982)
Solutions
- If you implement Sequencer yourself, make getBufferSize() return the same validated positive power-of-2 size AbstractSequencer enforces.
- Prefer extending AbstractSequencer rather than implementing Sequencer from scratch so the built-in validation applies.
- Fix mocks: stub getBufferSize() to return a valid size like 1024.
Example fix
// before
class MySequencer implements Sequencer {
public int getBufferSize() { return bufferSize; } // bufferSize field never set -> 0
}
// after
class MySequencer extends AbstractSequencer {
MySequencer(int size, WaitStrategy ws) { super(size, ws); } // validated here
} Defensive patterns
Strategy: validation
Validate before calling
int size = customSequencer.getBufferSize();
if (size < 1) throw new IllegalArgumentException("custom sequencer reports invalid bufferSize " + size); Prevention
- Extend AbstractSequencer instead of implementing Sequencer from scratch.
- Stub getBufferSize() on every Sequencer mock in tests.
When it happens
Trigger: Supplying a hand-written Sequencer implementation to new RingBuffer<>(factory, customSequencer) whose getBufferSize() returns 0 or a negative value; subclassing RingBuffer/RingBufferFields with a broken size.
Common situations: Custom sequencer subclasses added for instrumentation or testing that forget to initialise the bufferSize field; mocking a Sequencer without stubbing getBufferSize() (default mock returns 0).
Related errors
- bufferSize must be a power of 2
- bufferSize must not be less than 1
- bufferSize must not be less than 1
- bufferSize must be a power of 2
- n must be > 0 and < bufferSize
AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14).
Data as JSON: /api/errors/e4868e3a1a716a87.
Report an issue: GitHub.