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
- Restore the member's mark file, snapshot, and archive recordings together from a single consistent backup
- Let the member rejoin as a fresh member (clean cluster dir) and catch up via snapshot
- Verify archive recordings were not trimmed past the term base positions while the member was down
- 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
- Never trim archive recordings below the current term base position while members are catching up
- Restore mark file + archive + snapshot from one backup
- For a diverged member, prefer a clean rejoin over patching state
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
- unexpected image close during catchup: position=
- election in progress
- logSessionId was null, should always have a value
- potential new election in progress
- new leader detected due to commit position - memberId=
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)