{"record":{"id":"bae8290c598bf019","repo":"aeron-io/aeron","slug":"snapshot-ended-unexpectedly-image","errorCode":null,"errorMessage":"snapshot ended unexpectedly: ${image}","messagePattern":"snapshot ended unexpectedly: (.+?)","errorType":"exception","errorClass":"ClusterException","httpStatus":null,"severity":"error","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/ClusterToolOperator.java","lineNumber":823,"sourceCode":"                    Thread.yield();\n                }\n\n                final ConsensusModuleSnapshotAdapter adapter = new ConsensusModuleSnapshotAdapter(\n                    image, new ConsensusModuleSnapshotPrinter(out));\n\n                while (true)\n                {\n                    final int fragments = adapter.poll();\n                    if (adapter.isDone())\n                    {\n                        break;\n                    }\n\n                    if (0 == fragments)\n                    {\n                        if (image.isClosed())\n                        {\n                            throw new ClusterException(\"snapshot ended unexpectedly: \" + image);\n                        }\n\n                        archive.checkForErrorResponse();\n                        Thread.yield();\n                    }\n                }\n\n                out.println(\"Consensus Module Snapshot End:\" +\n                    \" memberId=\" + properties.memberId +\n                    \" recordingId=\" + entry.recordingId +\n                    \" length=\" + image.position());\n\n                if (null != postConsensusImageDescriber)\n                {\n                    postConsensusImageDescriber.accept(image, aeron);\n                }\n            }\n        }","sourceCodeStart":805,"sourceCodeEnd":841,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/ClusterToolOperator.java#L805-L841","documentation":"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.","triggerScenarios":"Running ClusterTool describeLatestConsensusModuleSnapshot (or equivalent) where the Image from the archive replay is closed before any fragment was received (fragments == 0 && image.isClosed()).","commonSituations":"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.","solutions":["Verify the snapshot recording referenced by the latest consensus module snapshot actually exists in the archive (check recording log and archive catalogue).","Check archive connectivity/credentials and that the archive is running during the replay.","Ensure no other process deleted or compacted archive segments; restore from backup if segments are missing.","Retry the tool operation; if it persists, re-snapshot the cluster so a valid snapshot exists."],"exampleFix":"// before\nClusterTool.describeLatestConsensusModuleSnapshot(ctx, out); // fails: snapshot recording missing\n// after\nRecordingLog recordingLog = new RecordingLog(clusterDir); // validate snapshot entries first\nRecordingLog.Snapshot snap = recordingLog.getLatestSnapshot(CONSENSUS_MODULE_SERVICE_ID);\nif (snap != null && archiveHasRecording(archive, snap.recordingId))\n{\n    ClusterTool.describeLatestConsensusModuleSnapshot(ctx, out);\n}","handlingStrategy":"try-catch","validationCode":"// before describing the snapshot\nRecordingLog log = new RecordingLog(clusterDir);\nRecordingLog.Entry latest = log.getLatestSnapshot(ConsensusModule.Configuration.CONSENSUS_MODULE_ID);\nif (latest == null) throw new IllegalStateException(\"no consensus module snapshot in recording log\");","typeGuard":null,"tryCatchPattern":"try\n{\n    ClusterTool.describeLatestConsensusModuleSnapshot(aeronArchiveContext, out);\n}\ncatch (ClusterException ex)\n{\n    if (ex.getMessage().startsWith(\"snapshot ended unexpectedly\"))\n    {\n        // archive recording missing/truncated: verify archive catalogue, re-snapshot\n    }\n    else throw ex;\n}","preventionTips":["Do not delete archive segments while a cluster retains snapshots referencing them.","Keep the archive running and reachable when running cluster tools.","Regularly re-snapshot so the latest snapshot is fresh and intact."],"tags":["aeron","cluster","archive","snapshot"],"backgroundTag":"unexpected-response-shape","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}