aeron-io/aeron · error · ClusterException

Aeron client must use a RethrowingErrorHandler

Error message

Aeron client must use a RethrowingErrorHandler

What it means

Thrown as ClusterException when the Aeron client used by the ClusteredServiceContainer has a subscriberErrorHandler that is not a RethrowingErrorHandler. The clustered service relies on exceptions from subscriber error handlers propagating up so log failures abort the container instead of being silently swallowed, which would corrupt cluster consistency.

Solutions

  1. Set aeron.context().subscriberErrorHandler(new RethrowingErrorHandler()) on the client context passed to the container
  2. Let the container create its own Aeron client (do not override the error handler) if you don't need custom handling
  3. Wrap custom handling around RethrowingErrorHandler by subclassing and rethrowing after logging
  4. If Aeron.Context defaults were changed, restore the default rethrowing subscriber error handler

Example fix

// before
Aeron.Context aeronCtx = new Aeron.Context()
    .subscriberErrorHandler(new LoggingErrorHandler());
aeron = Aeron.connect(aeronCtx);
// after
Aeron.Context aeronCtx = new Aeron.Context()
    .subscriberErrorHandler(new RethrowingErrorHandler());
aeron = Aeron.connect(aeronCtx);
Defensive patterns

Strategy: validation

Validate before calling

if (!(aeronCtx.subscriberErrorHandler() instanceof RethrowingErrorHandler)) {
    aeronCtx.subscriberErrorHandler(new RethrowingErrorHandler());
}

Type guard

boolean isRethrowing(Aeron.Context ctx) { return ctx.subscriberErrorHandler() instanceof RethrowingErrorHandler; }

Prevention

When it happens

Trigger: Creating the Aeron client with Aeron.connect(ctx) where ctx.subscriberErrorHandler(...) is set to a custom (non-rethrowing) handler, or a default handler that only logs; building the Aeron client inside the container with a pre-configured context carrying the wrong error handler; letting the container create its own client (ownsAeronClient) after overriding the handler.

Common situations: Embedding the container in an application that globally configures Aeron contexts with logging error handlers; copy-pasted driver/client context reuse; replacing RethrowingErrorHandler to suppress image errors without realizing the cluster requires rethrow semantics.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/04776cbe9253383e. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceContainer.java:949

            }

            if (null == aeron)
            {
                aeron = Aeron.connect(
                    new Aeron.Context()
                        .aeronDirectoryName(aeronDirectoryName)
                        .errorHandler(errorHandler)
                        .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE)
                        .awaitingIdleStrategy(YieldingIdleStrategy.INSTANCE)
                        .epochClock(epochClock)
                        .clientName(serviceName));

                ownsAeronClient = true;
            }

            if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler))
            {
                throw new ClusterException("Aeron client must use a RethrowingErrorHandler");
            }

            final ExpandableArrayBuffer tempBuffer = new ExpandableArrayBuffer();
            if (null == errorCounter)
            {
                errorCounter = ClusterCounters.allocateServiceErrorCounter(aeron, tempBuffer, clusterId, serviceId);
            }

            if (null == countedErrorHandler)
            {
                countedErrorHandler = new CountedErrorHandler(errorHandler, errorCounter);
                if (ownsAeronClient)
                {
                    aeron.context().errorHandler(countedErrorHandler);
                }
            }

            if (null == dutyCycleTracker)

View on GitHub (pinned to 6d60124e15)