LMAX-Exchange/disruptor · error · UnsupportedOperationException
Rewindable Exception thrown from a non-rewindable event hand
Error message
Rewindable Exception thrown from a non-rewindable event handler
What it means
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.
Source
Thrown at src/main/java/com/lmax/disruptor/BatchEventProcessor.java:310
{
if (batchRewindStrategy.handleRewindException(e, ++retriesAttempted) == REWIND)
{
return startOfBatchSequence;
}
else
{
retriesAttempted = 0;
throw e;
}
}
}
private static class NoRewindHandler implements RewindHandler
{
@Override
public long attemptRewindGetNextSequence(final RewindableException e, final long startOfBatchSequence)
{
throw new UnsupportedOperationException("Rewindable Exception thrown from a non-rewindable event handler", e);
}
}
}View on GitHub (pinned to c871ca4982)
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.
Example fix
// before
class MyHandler implements EventHandler<Event> {
public void onEvent(Event e, long seq, boolean endOfBatch) {
if (notReady()) throw new RewindableException(); // -> UnsupportedOperationException
}
}
// after
class MyHandler implements RewindableEventHandler<Event> {
public void onEvent(Event e, long seq, boolean endOfBatch) {
if (notReady()) throw new RewindableException(); // now rewound via BatchRewindStrategy
}
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(handler instanceof RewindableEventHandler) && exception instanceof RewindableException) {
throw new IllegalStateException("handler cannot rewind; fix wiring", exception);
} Type guard
static boolean canRewind(EventHandler<?> h) {
return h instanceof RewindableEventHandler;
} Try / catch
try { processor.run(); }
catch (UnsupportedOperationException e) {
if (e.getMessage() != null && e.getMessage().contains("non-rewindable")) {
// handler must implement RewindableEventHandler, or stop throwing RewindableException
} else throw e;
} Prevention
- Only throw RewindableException from handlers implementing RewindableEventHandler.
- Keep a code-review checklist item: new handlers that throw RewindableException must be rewindable.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- batchRewindStrategy cannot be null when building a BatchEven
- The event handler {} is not processing events.
- setDefaultExceptionHandler can not be used after handleExcep
- EventProcessor: {} is not a BatchEventProcessor and does not
- bufferSize must not be less than 1
AI-assisted analysis of LMAX-Exchange/disruptor@c871ca4982 (2026-08-14).
Data as JSON: /api/errors/a2e60068e1c9ab72.
Report an issue: GitHub.