Skip to main content
The @OnError annotation marks a method as a custom error handler inside a @ChangeStream class. When a handler method throws an exception, FlowWarden consults @OnError methods before the standard retry/DLQ chain, giving you full control over what happens next.

Basic Usage

Method Signature

@OnError methods must follow this exact signature:
Any other signature will cause a BeanCreationException at startup. The return type must be ErrorActionvoid is not supported. The parameter order is (Throwable, ChangeStreamContext), not the reverse.

Attribute

ErrorAction

The return value of an @OnError method tells FlowWarden what to do next:
RETRY and RETHROW both go through FlowWarden’s sequential retry engine — the stream does not advance to the next event during the retry sequence. See head-of-line blocking for the worked example and workarounds.

Exception Filtering

Typed Handlers

Target specific exception types using the value attribute:

Multiple Exception Types

A single handler can match multiple exception types:

Catch-All Handler

Omit value (or set it to {}) to create a catch-all that handles any unmatched exception:

Resolution Order

When multiple @OnError methods exist, FlowWarden picks the most specific match:
Given these handlers:

Comprehensive Example

@OnError methods always use the imperative signature — even in reactive streams. The reactive handler may return Mono.error(), but the error handler itself is synchronous and returns an ErrorAction directly.

How It Works

Validation Rules

FlowWarden validates @OnError methods at startup and will throw a BeanCreationException if:

Error Handler Safety

If an @OnError method itself throws an exception, FlowWarden catches it, logs the error, and falls back to ErrorAction.RETHROW. This prevents error handlers from crashing the stream.

Best Practices

  • Use SKIP for programming errors (validation failures, malformed data) that retrying won’t fix.
  • Use RETRY for known transient errors where you want to bypass the exception type checks of @RetryPolicy.noRetryOn.
  • Use DLQ to short-circuit retries when you can determine that an error is permanent (e.g., external service returns HTTP 404).
  • Use RETHROW as a safe default in catch-all handlers — it lets the standard retry/DLQ chain do its job.
  • Keep error handlers simple. Avoid calling external services or performing database operations in @OnError methods — they should be fast decision-makers, not processors.
Use ctx.getAttemptNumber() in your error handler to make decisions based on the retry count. For example, return RETRY on first attempt but DLQ on subsequent attempts.

See Also

@RetryPolicy

Configure exponential backoff retry for failed handlers

@DeadLetterQueue

Capture failed events for later investigation and reprocessing

Event Handlers

@OnChange, @OnInsert, @OnUpdate, @OnDelete handler reference

ChangeStreamContext

Runtime context including attempt number, event ID, and sendToDlq()