aeron-io/aeron · error · ClusterException

snapshot ended unexpectedly

Error message

snapshot ended unexpectedly: ${image}

What it means

While describing the latest consensus module snapshot, ClusterToolOperator polls the snapshot image for fragments; if a poll returns zero fragments and the image is closed, the snapshot ended without delivering its content, so it throws ClusterException "snapshot ended unexpectedly". This means the archive replay of the snapshot recording terminated prematurely.

Solutions

  1. Verify the snapshot recording referenced by the latest consensus module snapshot actually exists in the archive (check recording log and archive catalogue).
  2. Check archive connectivity/credentials and that the archive is running during the replay.
  3. Ensure no other process deleted or compacted archive segments; restore from backup if segments are missing.
  4. Retry the tool operation; if it persists, re-snapshot the cluster so a valid snapshot exists.

Example fix

// before
ClusterTool.describeLatestConsensusModuleSnapshot(ctx, out); // fails: snapshot recording missing
// after
RecordingLog recordingLog = new RecordingLog(clusterDir); // validate snapshot entries first
RecordingLog.Snapshot snap = recordingLog.getLatestSnapshot(CONSENSUS_MODULE_SERVICE_ID);
if (snap != null && archiveHasRecording(archive, snap.recordingId))
{
    ClusterTool.describeLatestConsensusModuleSnapshot(ctx, out);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before describing the snapshot
RecordingLog log = new RecordingLog(clusterDir);
RecordingLog.Entry latest = log.getLatestSnapshot(ConsensusModule.Configuration.CONSENSUS_MODULE_ID);
if (latest == null) throw new IllegalStateException("no consensus module snapshot in recording log");

Try / catch

try
{
    ClusterTool.describeLatestConsensusModuleSnapshot(aeronArchiveContext, out);
}
catch (ClusterException ex)
{
    if (ex.getMessage().startsWith("snapshot ended unexpectedly"))
    {
        // archive recording missing/truncated: verify archive catalogue, re-snapshot
    }
    else throw ex;
}

Prevention

When it happens

Trigger: Running ClusterTool describeLatestConsensusModuleSnapshot (or equivalent) where the Image from the archive replay is closed before any fragment was received (fragments == 0 && image.isClosed()).

Common situations: Snapshot recording in the archive was deleted or truncated; archive node crashed mid-replay; using a tool against a cluster dir whose archive no longer contains the referenced recording; network disconnect from the archive during replay.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ClusterToolOperator.java:823

                    Thread.yield();
                }

                final ConsensusModuleSnapshotAdapter adapter = new ConsensusModuleSnapshotAdapter(
                    image, new ConsensusModuleSnapshotPrinter(out));

                while (true)
                {
                    final int fragments = adapter.poll();
                    if (adapter.isDone())
                    {
                        break;
                    }

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

                        archive.checkForErrorResponse();
                        Thread.yield();
                    }
                }

                out.println("Consensus Module Snapshot End:" +
                    " memberId=" + properties.memberId +
                    " recordingId=" + entry.recordingId +
                    " length=" + image.position());

                if (null != postConsensusImageDescriber)
                {
                    postConsensusImageDescriber.accept(image, aeron);
                }
            }
        }

View on GitHub (pinned to 6d60124e15)