aeron-io/aeron · error · ClusterException
error counter must be supplied if aeron client is
Error message
error counter must be supplied if aeron client is
What it means
If the Aeron client is externally supplied, conclude() requires an errorCounter (CountedErrorHandler needs it to track errors); when a client is provided but errorCounter is null, the ClusterException "error counter must be supplied if aeron client is" is thrown. When the module owns the client it allocates the counter itself, so this only fires for user-supplied clients.
Solutions
- Call ctx.errorCounter(...) with a counter allocated via ClusterCounters.allocateVersioned(...) (CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID)
- Or let the module create its own client and counter by not calling ctx.aeron(...)
- If the client is external, also supply the matching countedErrorHandler/error log wiring expected by the cluster
Example fix
// before
ctx.aeron(externalClient); // errorCounter not set
// after
Counter errorCounter = ClusterCounters.allocateVersioned(aeron, buffer,
"Cluster Errors", CONSENSUS_MODULE_ERROR_COUNT_TYPE_ID, clusterId,
ConsensusModuleVersion.VERSION, ConsensusModuleVersion.GIT_SHA);
ctx.aeron(externalClient).errorCounter(errorCounter); Defensive patterns
Strategy: validation
Validate before calling
if (externalClientUsed && ctx.errorCounter() == null) {
throw new IllegalStateException("errorCounter must be supplied when passing an external Aeron client");
} Try / catch
try { ctx.conclude(); } catch (ClusterException e) { if (e.getMessage().contains("error counter must be supplied")) { /* allocate counter via ClusterCounters and retry setup */ } } Prevention
- When supplying ctx.aeron(...), also supply ctx.errorCounter(...) from ClusterCounters
- Or skip both and let the ConsensusModule own client + counters
- Keep counter allocation in a shared bootstrap helper so it is never forgotten
When it happens
Trigger: ctx.aeron(client) set (or errorCounter otherwise null after the client-owned allocation path) and ctx.errorCounter(...) never called before conclude().
Common situations: Embedding the ConsensusModule with a shared Aeron client but forgetting cluster counters wiring; copying Context setup from an owned-client example and adding an external client without adding errorCounter.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Aeron client must use a RethrowingErrorHandler
- Aeron client must use conductor agent invoker
- Aeron client must use a RethrowingErrorHandler
- Aeron client must use conductor agent invoker
- catalogFileSyncLevel
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/a0bc7228ddc7380d.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModule.java:1920
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);
}
}
if (null == moduleStateCounter)
{
final CountersReader counters = aeron.countersReader();
if (Aeron.NULL_VALUE != ClusterCounters.find(counters, CONSENSUS_MODULE_STATE_TYPE_ID, clusterId))
{
throw new ClusterException("existing consensus module detected for clusterId=" + clusterId);
}View on GitHub (pinned to 6d60124e15)