aeron-io/aeron · critical · ClusterEvent

- joinPosition= than logPosition=

Error message

${state} - joinPosition=${joinPosition}${inequality}than logPosition=${logPosition}

What it means

Election.verifyLogJoinPosition guards that the position at which a member is asked to join (or replay) the cluster log exactly equals the member's current logPosition. Aeron requires the log to be joined at the exact committed position so no entries are skipped or duplicated; any mismatch is a consensus/follower state bug and the election is aborted with a ClusterEvent.

Solutions

  1. Check the reported joinPosition vs logPosition values to see which member state is stale
  2. Restore the member from a recent snapshot or clean its cluster data directory and rejoin so it recovers via catch-up
  3. Ensure all members run the same Aeron version and cluster configuration (term-length, service Counters)
  4. If reproducible, file a bug with the election state trace — a mismatch here usually indicates an internal election bug

Example fix

// before: joining log with a computed position
ctx.logChannel() // joined with joinPosition != election.logPosition
// after: force the member to catch up before joining
if (joinPosition != logPosition) {
    // re-enter catch-up / reset from snapshot instead of throwing
    electionState(ElectionState.CATCHUP_TRANSITION);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (election.logPosition() != expectedJoinPosition) { /* re-enter catch-up instead of joining */ }

Try / catch

try { election.joinLog(joinPosition); } catch (ClusterEvent e) { agent.resetFromSnapshot(); }

Prevention

When it happens

Trigger: During election states such as FOLLOWER_BALLOT, CATCHUP_TRANSITION or LEADER_TRANSITION, Election computes a joinPosition passed to verifyLogJoinPosition; if it differs from this member's logPosition (because of stale snapshot state, a catchup that did not complete, or an internal bookkeeping bug), the exception is thrown.

Common situations: Corrupt or outdated snapshot/recovery state on a member, interrupted catch-up, versions of the cluster members out of sync, or a bug in custom election/consensus-module extensions leading members to disagree on the log position.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/Election.java:1544

        logLeadershipTermId = leadershipTermId;
    }

    private void updateRecordingLogForReplication(
        final long leadershipTermId,
        final long termBaseLogPosition,
        final long logPosition,
        final long nowNs)
    {
        ensureRecordingLogCoherent(leadershipTermId, termBaseLogPosition, logPosition, nowNs);
        logLeadershipTermId = leadershipTermId;
    }

    private void verifyLogJoinPosition(final String state, final long joinPosition)
    {
        if (joinPosition != logPosition)
        {
            final String inequality = joinPosition < logPosition ? " less " : " greater ";
            throw new ClusterEvent(
                state + " - joinPosition=" + joinPosition + inequality + "than logPosition=" + logPosition);
        }
    }

    private boolean hasUpdateIntervalExpired(final long nowNs, final long intervalNs)
    {
        return hasIntervalExpired(nowNs, timeOfLastUpdateNs, intervalNs);
    }

    private boolean hasIntervalExpired(
        final long nowNs, final long previousTimestampForIntervalNs, final long intervalNs)
    {
        return (nowNs - previousTimestampForIntervalNs) >= intervalNs;
    }

    private void logStateChange(
        final int memberId,
        final ElectionState oldState,

View on GitHub (pinned to 6d60124e15)