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

  1. Check the leader's health and logs around the failure time; the leader likely terminated its log publication
  2. Verify network stability between follower and leader (timeouts, firewalls, TCP resets)
  3. Confirm all nodes run compatible Aeron versions and consistent cluster/channel configuration
  4. 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

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


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)