LMAX-Exchange/disruptor · error · IllegalStateException
All event handlers must be added before calling starts.
Error message
All event handlers must be added before calling starts.
What it means
Thrown by Disruptor's internal checkNotStarted guard: you attempted to modify the consumer graph (handleEventsWith, handleEventsWithWorkerPool, after, handleExceptionsFor, setDefaultExceptionHandler, etc.) after Disruptor.start() was already called. Once started, the wiring is frozen because sequences and barriers have been handed to running threads and mutating them is unsafe.
Source
Thrown at src/main/java/com/lmax/disruptor/dsl/Disruptor.java:596
}
EventHandlerGroup<T> createEventProcessors(
final Sequence[] barrierSequences, final EventProcessorFactory<T>[] processorFactories)
{
final EventProcessor[] eventProcessors = new EventProcessor[processorFactories.length];
for (int i = 0; i < processorFactories.length; i++)
{
eventProcessors[i] = processorFactories[i].createEventProcessor(ringBuffer, barrierSequences);
}
return handleEventsWith(eventProcessors);
}
private void checkNotStarted()
{
if (started.get())
{
throw new IllegalStateException("All event handlers must be added before calling starts.");
}
}
private void checkOnlyStartedOnce()
{
if (!started.compareAndSet(false, true))
{
throw new IllegalStateException("Disruptor.start() must only be called once.");
}
}
@Override
public String toString()
{
return "Disruptor{" +
"ringBuffer=" + ringBuffer +
", started=" + started +
", threadFactory=" + threadFactory +View on GitHub (pinned to c871ca4982)
Solutions
- Register all handlers before calling start(); move every handleEventsWith/after call ahead of start().
- For genuinely dynamic consumers, create a separate Disruptor or use a WorkPool/worker handler that pulls work dynamically instead of re-wiring.
- Review startup order (constructor vs @PostConstruct vs main) so wiring completes first.
Example fix
// before disruptor.handleEventsWith(primaryHandler); disruptor.start(); disruptor.handleEventsWith(extraHandler); // IllegalStateException // after disruptor.handleEventsWith(primaryHandler); disruptor.handleEventsWith(extraHandler); disruptor.start();
Defensive patterns
Strategy: validation
Validate before calling
if (disruptor.hasStarted()) { // or track your own flag
throw new IllegalStateException("cannot add handlers after start");
}
disruptor.handleEventsWith(handler); Prevention
- Complete all handleEventsWith/after/workerPool wiring in one init method that ends with start().
- For dynamic consumers, use worker pools or a separate Disruptor instance instead of post-start wiring.
When it happens
Trigger: Calling disruptor.handleEventsWith(...) (or any DSL mutation) after disruptor.start() has run — e.g. lazy registration of an extra consumer triggered by the first event, or a scheduled task adding handlers.
Common situations: Dynamically adding consumers at runtime (not supported by Disruptor's DSL); initialization order bug where start() is called in a constructor/@PostConstruct and handlers registered later; retry logic that re-wires handlers after startup.
Related errors
- Thread is already running
- setDefaultExceptionHandler can not be used after handleExcep
- Disruptor.start() must only be called once.
- The event handler {} is not processing events.
- Already running
AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14).
Data as JSON: /api/errors/37969523cb3cb9de.
Report an issue: GitHub.