aeron-io/aeron · error · IllegalStateException

election in progress

Error message

election in progress

What it means

enterElection starts a new cluster election. The module allows only one election at a time; if enterElection is called while an election object is already active, IllegalStateException is thrown to signal the internal state machine was violated.

Solutions

  1. Capture full agent logs to see why enterElection was re-entered (reason string is logged)
  2. Check for network flapping causing repeated end-of-stream signals
  3. Update Aeron to the latest patch release; several election re-entry races have been fixed
  4. Restart the node to clear the stuck election state

Example fix

// before: re-entering election without checking state
agent.enterElection(isEos, reason);

// after: guard with election null check
if (null == election)
{
    agent.enterElection(isEos, reason);
}
Defensive patterns

Strategy: try-catch

Try / catch

catch (IllegalStateException e)
{
    if ("election in progress".equals(e.getMessage()))
    {
        log.warn("election re-entry attempt ignored");
        return; // let the active election finish
    }
    throw e;
}

Prevention

When it happens

Trigger: Thrown when election != null upon entering enterElection, e.g. an end-of-stream event or failure triggers a second election while the current one is still in progress.

Common situations: Leader and follower connectivity flapping, generating repeated end-of-stream events; a bug or race in election completion logic; concurrent failure events arriving during an active election.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModuleAgent.java:3064

        }

        if (!uncommittedPreviousState.isEmpty())
        {
            uncommittedPreviousState.pollLong();
            final ConsensusModule.State committedState = ConsensusModule.State.get(uncommittedPreviousState.pollLong());
            if (ConsensusModule.State.CLOSED != state)
            {
                state(committedState, "rollback");
            }
        }
        uncommittedPreviousState.clear();
    }

    private void enterElection(final boolean isLogEndOfStream, final String reason)
    {
        if (null != election)
        {
            throw new IllegalStateException("election in progress");
        }

        role(Cluster.Role.FOLLOWER);

        final long leadershipTermId = this.leadershipTermId;
        final RecordingLog.Entry termEntry = recordingLog.findTermEntry(leadershipTermId);
        final long termBaseLogPosition = null != termEntry ?
            termEntry.termBaseLogPosition : recoveryPlan.lastTermBaseLogPosition();
        final long appendedPosition = null != appendPosition ?
            appendPosition.get() : max(recoveryPlan.appendedLogPosition(), logRecordingStopPosition);
        final long commitPosition = this.commitPosition.getPlain();

        logNewElection(memberId, leadershipTermId, commitPosition, appendedPosition, reason);
        ctx.countedErrorHandler().onError(new ClusterEvent(reason));

        election = new Election(
            false,
            isLogEndOfStream ? leaderMember.id() : NULL_VALUE,

View on GitHub (pinned to 6d60124e15)