aeron-io/aeron · critical · ClusterEvent
unexpected image close during catchup: position=
Error message
unexpected image close during catchup: position=<position>
What it means
During leader catchup, a follower polls the log image to replay missing entries. Aeron throws this ClusterEvent when the image carrying the recording is unexpectedly closed while catchup fragments are still expected, meaning the log stream ended before the follower reached the leader's position.
Solutions
- Check the leader's health and logs around the failure time; the leader likely terminated its log publication
- Verify network stability between follower and leader (timeouts, firewalls, TCP resets)
- Confirm all nodes run compatible Aeron versions and consistent cluster/channel configuration
- Restart the follower node so it rejoins and triggers a fresh election/catchup cycle
Example fix
// before: catchup poll with no guard against repeated failure
logAdapter.poll(limitPosition);
// after: wrap agent failure so cluster restarts the node cleanly
try
{
workCount += logAdapter.poll(limitPosition);
}
catch (ClusterEvent e)
{
ctx.errorLog().log(e);
consensusModuleAgent.close(); // allow re-election and rejoin
} Defensive patterns
Strategy: retry
Validate before calling
// verify cluster connectivity before joining
if (!InetAddress.getByName(clusterMemberHost).isReachable(2000))
{
throw new IllegalStateException("cannot reach cluster member");
} Try / catch
catch (ClusterEvent e)
{
log.error("catchup image closed, rejoining cluster", e);
clusterContainer.close();
// re-launch container to trigger fresh election
} Prevention
- Monitor leader health and member connectivity continuously
- Keep all nodes on the same Aeron version
- Ensure stable, low-loss network between cluster members
When it happens
Trigger: Thrown in ConsensusModuleAgent while polling for catchup when logAdapter.poll() returns 0 fragments and logAdapter.isImageClosed() is true.
Common situations: Leader's recording publication closed or leader failed mid-catchup; network disconnect ending the image; archive/recording ended prematurely; a new election starting concurrently.
Related errors
- no catchup progress: commitPosition=
- election in progress
- logSessionId was null, should always have a value
- potential new election in progress
- invalid newLeadershipTerm - this.appendPosition=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/112884aa21f4fb44.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModuleAgent.java:1993
int catchupPoll(final long limitPosition, final long nowNs)
{
int workCount = 0;
if (ConsensusModule.State.ACTIVE == state || ConsensusModule.State.SUSPENDED == state)
{
if (null == appendPosition)
{
throw new ClusterEvent(
"unexpected recording stop during catchup: position=" + logAdapter.position());
}
final long currentAppendPosition = appendPosition.get();
final int fragments = logAdapter.poll(min(currentAppendPosition, limitPosition));
workCount += fragments;
if (0 == fragments && logAdapter.isImageClosed())
{
throw new ClusterEvent(
"unexpected image close during catchup: position=" + logAdapter.position());
}
workCount += updateFollowerPosition(
election.leader().publication(),
nowNs,
leadershipTermId,
currentAppendPosition,
APPEND_POSITION_FLAG_CATCHUP);
commitPosition.proposeMaxRelease(logAdapter.position());
}
if (nowNs > (timeOfLastAppendPositionUpdateNs + leaderHeartbeatTimeoutNs) &&
ConsensusModule.State.ACTIVE == state)
{
throw new ClusterEvent(
"no catchup progress:" +
" commitPosition=" + commitPosition.getPlain() +View on GitHub (pinned to 6d60124e15)