aeron-io/aeron · critical · ClusterException

invalid newLeadershipTerm - this.appendPosition=

Error message

invalid newLeadershipTerm - this.appendPosition=${appendPosition} < termBaseLogPosition=${termBaseLogPosition} and nextLeadershipTermId=${nextLeadershipTermId}, logLeadershipTermId=${logLeadershipTermId}, nextTermBaseLogPosition=${nextTermBaseLogPosition}, nextLogPosition=${nextLogPosition}, leadershipTermId=${leadershipTermId}, termBaseLogPosition=${termBaseLogPosition}, logPosition=${logPosition}, commitPosition=${commitPosition}, leaderRecordingId=${leaderRecordingId}, leaderMemberId=${leaderMemberId}, logSessionId=${logSessionId}, isStartup=${isStartup}

What it means

In onNewLeadershipTerm, a follower received a new leadership term whose termBaseLogPosition is greater than the follower's own appendPosition while nextLeadershipTermId does not follow from the follower's known log — i.e. the leader's log position/term bookkeeping is inconsistent with the follower's log, so the election aborts with full state dumped for diagnosis.

Solutions

  1. Restore the member's mark file, snapshot, and archive recordings together from a single consistent backup
  2. Let the member rejoin as a fresh member (clean cluster dir) and catch up via snapshot
  3. Verify archive recordings were not trimmed past the term base positions while the member was down
  4. Upgrade Aeron if on an old version; some election position edge cases were fixed

Example fix

// before: mark file from backup A, archive recordings from backup B
ClusterException: invalid newLeadershipTerm - this.appendPosition=... < termBaseLogPosition=...
// after: restore the complete consistent set (mark file + snapshot + recordings) from one backup, then restart the member
Defensive patterns

Strategy: validation

Validate before calling

// Before rejoining, verify the member's archive recordings cover the leader's term base positions
// compare mark file log positions and archive recording start positions with the leader's before starting

Try / catch

try {
    clusterMember.start();
} catch (ClusterException e) {
    if (e.getMessage().startsWith("invalid newLeadershipTerm")) {
        // wipe member state and rejoin fresh via snapshot
        cleanJoin();
    } else { throw e; }
}

Prevention

When it happens

Trigger: A follower's appendPosition (local recorded log position) is behind the leader's termBaseLogPosition and the incoming nextLeadershipTermId/logLeadershipTermId do not line up — e.g. the follower's log recording was trimmed/deleted, or its mark file/snapshot came from a different log history.

Common situations: Restoring a snapshot but keeping a mismatched (older/newer) mark file or archive; purged/truncated archive segments the leader assumes exist; joining a cluster whose log starts at positions the member never recorded.

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/6ef4a1934e2fc9e7. Report an issue: GitHub.

Appendix: source

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

                            // is already known. We could look it up from the recording log only to write
                            // it back again...
                            replicationTermBaseLogPosition = NULL_VALUE;
                            state(FOLLOWER_LOG_REPLICATION, nowNs(ctx), "");
                        }
                        else if (appendPosition == nextTermBaseLogPosition)
                        {
                            if (NULL_POSITION != nextLogPosition)
                            {
                                replicationLeadershipTermId = nextLeadershipTermId;
                                replicationStopPosition = nextLogPosition;
                                replicationTermBaseLogPosition = nextTermBaseLogPosition;
                                state(FOLLOWER_LOG_REPLICATION, nowNs(ctx), "");
                            }
                        }
                    }
                    else
                    {
                        throw new ClusterException(
                            "invalid newLeadershipTerm - this.appendPosition=" + appendPosition +
                            " < termBaseLogPosition=" + termBaseLogPosition +
                            " and nextLeadershipTermId=" + nextLeadershipTermId +
                            ", logLeadershipTermId=" + logLeadershipTermId +
                            ", nextTermBaseLogPosition=" + nextTermBaseLogPosition +
                            ", nextLogPosition=" + nextLogPosition +
                            ", leadershipTermId=" + leadershipTermId +
                            ", termBaseLogPosition=" + termBaseLogPosition +
                            ", logPosition=" + logPosition +
                            ", commitPosition=" + commitPosition +
                            ", leaderRecordingId=" + leaderRecordingId +
                            ", leaderMemberId=" + leaderMemberId +
                            ", logSessionId=" + logSessionId +
                            ", isStartup=" + isStartup);
                    }
                }
                else
                {

View on GitHub (pinned to 6d60124e15)