{"record":{"id":"e4868e3a1a716a87","repo":"LMAX-Exchange/disruptor","slug":"thread-is-already-running-e4868e","errorCode":null,"errorMessage":"Thread is already running","messagePattern":"Thread is already running","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/lmax/disruptor/NoOpEventProcessor.java","lineNumber":63,"sourceCode":"\n    @Override\n    public void halt()\n    {\n        running.set(false);\n    }\n\n    @Override\n    public boolean isRunning()\n    {\n        return running.get();\n    }\n\n    @Override\n    public void run()\n    {\n        if (!running.compareAndSet(false, true))\n        {\n            throw new IllegalStateException(\"Thread is already running\");\n        }\n    }\n\n    /**\n     * Sequence that follows (by wrapping) another sequence\n     */\n    private static final class SequencerFollowingSequence extends Sequence\n    {\n        private final RingBuffer<?> sequencer;\n\n        private SequencerFollowingSequence(final RingBuffer<?> sequencer)\n        {\n            super(Sequencer.INITIAL_CURSOR_VALUE);\n            this.sequencer = sequencer;\n        }\n\n        @Override\n        public long get()","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/LMAX-Exchange/disruptor/blob/c871ca49826a6be7ada6957f6fbafcfecf7b1f87/src/main/java/com/lmax/disruptor/NoOpEventProcessor.java#L45-L81","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nclass MySequencer implements Sequencer {\n    public int getBufferSize() { return bufferSize; } // bufferSize field never set -> 0\n}\n\n// after\nclass MySequencer extends AbstractSequencer {\n    MySequencer(int size, WaitStrategy ws) { super(size, ws); } // validated here\n}","handlingStrategy":"validation","validationCode":"int size = customSequencer.getBufferSize();\nif (size < 1) throw new IllegalArgumentException(\"custom sequencer reports invalid bufferSize \" + size);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Extend AbstractSequencer instead of implementing Sequencer from scratch.","Stub getBufferSize() on every Sequencer mock in tests."],"tags":["disruptor","ring-buffer","custom-sequencer","defensive-check","validation"],"backgroundTag":null,"analyzedSha":"c871ca49826a6be7ada6957f6fbafcfecf7b1f87","analyzedAt":"2026-08-14T14:22:36.358Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}