aeron-io/aeron · error · ClusterException

Aeron client must use a RethrowingErrorHandler

Error message

Aeron client must use a RethrowingErrorHandler

What it means

When an external Aeron client is supplied, conclude() verifies that its subscriberErrorHandler is a RethrowingErrorHandler. Subscriber errors in the cluster must propagate to the cluster's error handling rather than being swallowed, so a client configured with a different (e.g. logging) subscriber error handler is rejected with a ClusterException.

Solutions

  1. Configure the shared Aeron client with .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE)
  2. Or let the ConsensusModule create its own Aeron client (do not call ctx.aeron(...)) — it installs a rethrowing handler automatically
  3. Wrap your custom logic outside the subscriber error handler path (e.g. in a distinct client used by other subscriptions)

Example fix

// before
Aeron aeron = Aeron.connect(new Aeron.Context()
    .subscriberErrorHandler(err -> log.warn("error", err)));
ctx.aeron(aeron);
// after
Aeron aeron = Aeron.connect(new Aeron.Context()
    .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE));
ctx.aeron(aeron);
Defensive patterns

Strategy: validation

Validate before calling

if (!(aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler)) {
    throw new IllegalStateException("shared Aeron client must use RethrowingErrorHandler for the cluster");
}

Type guard

boolean usesRethrowingHandler(Aeron aeron) { return aeron.context().subscriberErrorHandler() instanceof RethrowingErrorHandler; }

Try / catch

try { ctx.conclude(); } catch (ClusterException e) { if (e.getMessage().contains("RethrowingErrorHandler")) { /* reconnect ctx with a compliant client */ } }

Prevention

When it happens

Trigger: Passing a user-created Aeron client via ctx.aeron(client) where the Aeron.Context had subscriberErrorHandler set to something other than RethrowingErrorHandler.INSTANCE.

Common situations: Sharing an application-wide Aeron client with the cluster after installing a custom subscriber error handler; enabling default Aeron error handling for other subscriptions and reusing the same client for the ConsensusModule.

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/5483a4648a0785b9. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java:1910

                    errorCounter = ClusterCounters.allocateVersioned(
                        aeron,
                        buffer,
                        "Cluster Errors",
                        CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID,
                        clusterId,
                        ConsensusModuleVersion.VERSION,
                        ConsensusModuleVersion.GIT_SHA);
                }
            }

            if (null == ingressChannel)
            {
                throw new ClusterException("ingressChannel must be specified");
            }

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

            if (!aeron.context().useConductorAgentInvoker())
            {
                throw new ClusterException("Aeron client must use conductor agent invoker");
            }

            if (null == errorCounter)
            {
                throw new ClusterException("error counter must be supplied if aeron client is");
            }

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

View on GitHub (pinned to 6d60124e15)