aeron-io/aeron · error · ClusterException

existing consensus module detected for clusterId=

Error message

existing consensus module detected for clusterId=<clusterId>

What it means

During ConsensusModule startup (conclude/onStart), the module checks the Aeron driver's counters for an existing 'Consensus Module state' counter matching the configured clusterId. If one already exists, another consensus module instance is (or was) using the same driver/media driver for this cluster, so a second module would corrupt cluster state. Aeron throws ClusterException to abort the duplicate startup.

Solutions

  1. Ensure the previous ConsensusModule/Aeron client is fully closed (close() in finally/shutdown hook) before starting a new one.
  2. Use a distinct clusterId for each cluster/member, or connect to a different (dedicated) media driver.
  3. If restarting after a crash, terminate the old media driver or clear its counters; for embedded drivers recreate the driver process.
  4. If moduleStateCounter was captured externally, pass the existing counter instead of letting conclude() detect a duplicate.

Example fix

// before
Aeron aeron = Aeron.connect();
ConsensusModule module = ConsensusModule.launch(moduleCtx.aeron(aeron).clusterId(0));
ConsensusModule second = ConsensusModule.launch(moduleCtx2.aeron(aeron).clusterId(0)); // throws
// after
ConsensusModule second = ConsensusModule.launch(moduleCtx2.aeron(aeron).clusterId(1)); // distinct clusterId
Defensive patterns

Strategy: try-catch

Validate before calling

// before launch
CountersReader counters = aeron.countersReader();
if (Aeron.NULL_VALUE != ClusterCounters.find(counters, CONSENSUS_MODULE_STATE_TYPE_ID, clusterId)) {
    throw new IllegalStateException("consensus module already running for clusterId=" + clusterId);
}

Try / catch

try { ConsensusModule.launch(ctx); } catch (ClusterException e) { if (e.getMessage().contains("existing consensus module detected")) { closePreviousModuleAndRetry(); } else { throw e; } }

Prevention

When it happens

Trigger: Launching a second ConsensusModule with the same clusterId while a previous one is still connected to the same Aeron driver, or restarting after a crash that left the stale counter allocated in a still-running embedded driver.

Common situations: Accidentally starting two nodes in the same JVM/driver during testing; a previous ConsensusModule not fully closed (missing close()) before re-launching; embedded-driver test harnesses reusing a driver across runs; container restart where the media driver survives.

Related errors


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

Appendix: source

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

            {
                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);
                }

                moduleStateCounter = ClusterCounters.allocate(
                    aeron, buffer, "Consensus Module state", CONSENSUS_MODULE_STATE_TYPE_ID, clusterId);
            }
            validateCounterTypeId(aeron, moduleStateCounter, CONSENSUS_MODULE_STATE_TYPE_ID);


            if (null == electionStateCounter)
            {
                electionStateCounter = ClusterCounters.allocate(
                    aeron, buffer, "Cluster election state", ELECTION_STATE_TYPE_ID, clusterId);
            }
            validateCounterTypeId(aeron, electionStateCounter, ELECTION_STATE_TYPE_ID);

            if (null == electionCounter)
            {
                electionCounter = ClusterCounters.allocate(

View on GitHub (pinned to 6d60124e15)