aeron-io/aeron · critical · ClusterException

snapshot ended unexpectedly

Error message

snapshot ended unexpectedly: ${image}

What it means

Thrown as ClusterException while a leader records a snapshot: the polling loop on the snapshot publication image found zero fragments and the image is closed, meaning the replay/recording of the snapshot ended before reaching the expected position. The snapshot transfer did not complete as planned.

Solutions

  1. Check the archive/driver error log for the underlying replay failure and fix it
  2. Verify the snapshot recording in the archive is complete and not truncated
  3. Confirm replay channel/stream IDs in the recovery plan match the archive configuration
  4. Restart the cluster node so recovery re-attempts loading from the snapshot
Defensive patterns

Strategy: retry

Try / catch

try { awaitSnapshotCompletion(image); }
catch (ClusterException ex) {
    // image closed early: re-request replay from archive after checking archive health
    retrySnapshotLoadWithBackoff();
}

Prevention

When it happens

Trigger: During loadSnapshot/awaitSnapshot, the archive replay image is closed while 0 fragments were polled in the last cycle and the expected position was not reached; replay stream terminated by the archive; network/channel error between archive and the container.

Common situations: Archive error or media driver failure while streaming a snapshot; snapshot recording truncated in the archive; replay channel/URI misconfiguration causing the replay to close immediately; archive session timeout.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java:970

    }

    private void loadState(final Image image, final AeronArchive archive)
    {
        final ServiceSnapshotLoader snapshotLoader = new ServiceSnapshotLoader(image, this);
        while (true)
        {
            final int fragments = snapshotLoader.poll();
            if (snapshotLoader.isDone())
            {
                break;
            }

            if (0 == fragments)
            {
                archive.checkForErrorResponse();
                if (image.isClosed())
                {
                    throw new ClusterException("snapshot ended unexpectedly: " + image);
                }
            }

            idle(fragments);
        }

        final int appVersion = snapshotLoader.appVersion();
        if (!ctx.appVersionValidator().isVersionCompatible(ctx.appVersion(), appVersion))
        {
            throw new ClusterException(
                "incompatible app version: " + SemanticVersion.toString(ctx.appVersion()) +
                " snapshot=" + SemanticVersion.toString(appVersion));
        }

        timeUnit = snapshotLoader.timeUnit();
    }

    private long onTakeSnapshot(final long logPosition, final long leadershipTermId)

View on GitHub (pinned to 6d60124e15)