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

  1. Register all handlers before calling start(); move every handleEventsWith/after call ahead of start().
  2. For genuinely dynamic consumers, create a separate Disruptor or use a WorkPool/worker handler that pulls work dynamically instead of re-wiring.
  3. 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

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


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