{"record":{"id":"a2e60068e1c9ab72","repo":"LMAX-Exchange/disruptor","slug":"rewindable-exception-thrown-from-a-non-rewindable","errorCode":null,"errorMessage":"Rewindable Exception thrown from a non-rewindable event handler","messagePattern":"Rewindable Exception thrown from a non-rewindable event handler","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"src/main/java/com/lmax/disruptor/BatchEventProcessor.java","lineNumber":310,"sourceCode":"        {\n            if (batchRewindStrategy.handleRewindException(e, ++retriesAttempted) == REWIND)\n            {\n                return startOfBatchSequence;\n            }\n            else\n            {\n                retriesAttempted = 0;\n                throw e;\n            }\n        }\n    }\n\n    private static class NoRewindHandler implements RewindHandler\n    {\n        @Override\n        public long attemptRewindGetNextSequence(final RewindableException e, final long startOfBatchSequence)\n        {\n            throw new UnsupportedOperationException(\"Rewindable Exception thrown from a non-rewindable event handler\", e);\n        }\n    }\n}","sourceCodeStart":292,"sourceCodeEnd":313,"githubUrl":"https://github.com/LMAX-Exchange/disruptor/blob/c871ca49826a6be7ada6957f6fbafcfecf7b1f87/src/main/java/com/lmax/disruptor/BatchEventProcessor.java#L292-L313","documentation":"Thrown by the NoRewindHandler inside BatchEventProcessor when a RewindableException propagates from an event handler that does not implement RewindableEventHandler. Rewind (replay a batch) is only possible for handlers that explicitly opt in, because only they know how to make their processing idempotent; for plain EventHandler/BatchEventHandler the processor cannot safely retry.","triggerScenarios":"Registering a plain EventHandler (or BatchEventHandler) that throws a RewindableException (or an exception wrapping one) — the processor consults its rewind handler, gets NoRewindHandler because instanceof RewindableEventHandler was false, and this UnsupportedOperationException escapes.","commonSituations":"Developer copies RewindableException usage from another codebase where the handler implemented RewindableEventHandler; refactoring a rewindable handler into a plain one while keeping the exception; a downstream library throwing RewindableException into a non-rewindable handler.","solutions":["Make the handler implement RewindableEventHandler (onEvent plus onBatchStart/rewind support) so rewind is legitimate.","If rewind is not needed, throw a different exception type and handle it via an ExceptionHandler instead of RewindableException.","Audit the handler chain: the exception may originate in a delegate/inner handler that still throws RewindableException."],"exampleFix":"// before\nclass MyHandler implements EventHandler<Event> {\n    public void onEvent(Event e, long seq, boolean endOfBatch) {\n        if (notReady()) throw new RewindableException(); // -> UnsupportedOperationException\n    }\n}\n\n// after\nclass MyHandler implements RewindableEventHandler<Event> {\n    public void onEvent(Event e, long seq, boolean endOfBatch) {\n        if (notReady()) throw new RewindableException(); // now rewound via BatchRewindStrategy\n    }\n}","handlingStrategy":"type-guard","validationCode":"if (!(handler instanceof RewindableEventHandler) && exception instanceof RewindableException) {\n    throw new IllegalStateException(\"handler cannot rewind; fix wiring\", exception);\n}","typeGuard":"static boolean canRewind(EventHandler<?> h) {\n    return h instanceof RewindableEventHandler;\n}","tryCatchPattern":"try { processor.run(); }\ncatch (UnsupportedOperationException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"non-rewindable\")) {\n        // handler must implement RewindableEventHandler, or stop throwing RewindableException\n    } else throw e;\n}","preventionTips":["Only throw RewindableException from handlers implementing RewindableEventHandler.","Keep a code-review checklist item: new handlers that throw RewindableException must be rewindable."],"tags":["disruptor","rewind","event-handler","api-misuse","unsupported-operation"],"backgroundTag":null,"analyzedSha":"c871ca49826a6be7ada6957f6fbafcfecf7b1f87","analyzedAt":"2026-08-14T14:22:36.358Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}