{"record":{"id":"001559548bdb85d8","repo":"aeron-io/aeron","slug":"snapshot-ended-unexpectedly-image-clusteredserviceagent","errorCode":null,"errorMessage":"snapshot ended unexpectedly: ${image}","messagePattern":"snapshot ended unexpectedly: (.+?)","errorType":"console","errorClass":"ClusterException","httpStatus":null,"severity":"critical","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java","lineNumber":970,"sourceCode":"    }\n\n    private void loadState(final Image image, final AeronArchive archive)\n    {\n        final ServiceSnapshotLoader snapshotLoader = new ServiceSnapshotLoader(image, this);\n        while (true)\n        {\n            final int fragments = snapshotLoader.poll();\n            if (snapshotLoader.isDone())\n            {\n                break;\n            }\n\n            if (0 == fragments)\n            {\n                archive.checkForErrorResponse();\n                if (image.isClosed())\n                {\n                    throw new ClusterException(\"snapshot ended unexpectedly: \" + image);\n                }\n            }\n\n            idle(fragments);\n        }\n\n        final int appVersion = snapshotLoader.appVersion();\n        if (!ctx.appVersionValidator().isVersionCompatible(ctx.appVersion(), appVersion))\n        {\n            throw new ClusterException(\n                \"incompatible app version: \" + SemanticVersion.toString(ctx.appVersion()) +\n                \" snapshot=\" + SemanticVersion.toString(appVersion));\n        }\n\n        timeUnit = snapshotLoader.timeUnit();\n    }\n\n    private long onTakeSnapshot(final long logPosition, final long leadershipTermId)","sourceCodeStart":952,"sourceCodeEnd":988,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java#L952-L988","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the archive/driver error log for the underlying replay failure and fix it","Verify the snapshot recording in the archive is complete and not truncated","Confirm replay channel/stream IDs in the recovery plan match the archive configuration","Restart the cluster node so recovery re-attempts loading from the snapshot"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { awaitSnapshotCompletion(image); }\ncatch (ClusterException ex) {\n    // image closed early: re-request replay from archive after checking archive health\n    retrySnapshotLoadWithBackoff();\n}","preventionTips":["Monitor archive error responses (archive.checkForErrorResponse equivalents)","Verify replay channel/stream configuration matches the recovery plan","Keep archive recordings intact; check catalog after unclean shutdown"],"tags":["aeron","archive","snapshot","replay"],"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-16T04:17:20.429Z"}