{"record":{"id":"011a66f59da98ffa","repo":"aeron-io/aeron","slug":"cluster-log-must-be-contiguous-for-joining-image","errorCode":null,"errorMessage":"Cluster log must be contiguous for joining image: expectedPosition=<logPosition> joinPosition=<image.joinPosition()>","messagePattern":"Cluster log must be contiguous for joining image: expectedPosition=<logPosition> joinPosition=<image\\.joinPosition\\(\\)>","errorType":"exception","errorClass":"ClusterException","httpStatus":null,"severity":"critical","filePath":"aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java","lineNumber":865,"sourceCode":"        if (Role.LEADER != activeLog.role)\n        {\n            disconnectEgress(ctx.countedErrorHandler());\n        }\n\n        this.standbySnapshotFlags = activeLog.isStandby ? CLUSTER_ACTION_FLAGS_STANDBY_SNAPSHOT :\n            CLUSTER_ACTION_FLAGS_DEFAULT;\n\n        final String channel = new ChannelUriStringBuilder(activeLog.channel)\n            .alias(subscriptionAlias)\n            .build();\n\n        Subscription logSubscription = aeron.addSubscription(channel, activeLog.streamId);\n        try\n        {\n            final Image image = awaitImage(activeLog.sessionId, logSubscription);\n            if (image.joinPosition() != logPosition)\n            {\n                throw new ClusterException(\"Cluster log must be contiguous for joining image: \" +\n                    \"expectedPosition=\" + logPosition + \" joinPosition=\" + image.joinPosition());\n            }\n\n            if (activeLog.logPosition != logPosition)\n            {\n                throw new ClusterException(\"Cluster log must be contiguous for active log event: \" +\n                    \"expectedPosition=\" + logPosition + \" eventPosition=\" + activeLog.logPosition);\n            }\n\n            logAdapter.image(image);\n            logAdapter.maxLogPosition(activeLog.maxLogPosition);\n            logSubscription = null;\n\n            final long id = ackId++;\n            while (!consensusModuleProxy.ack(activeLog.logPosition, clusterTime, id, NULL_VALUE, serviceId))\n            {\n                idle();\n            }","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java#L847-L883","documentation":"Thrown as ClusterException when the image joined on the cluster log subscription does not have a joinPosition equal to the expected logPosition the service container recovered to. The cluster log must be contiguous: a service container can only join a log image that starts exactly where its recovered state ends, otherwise log entries would be skipped or replayed.","triggerScenarios":"During log join/await after recovery, the active log image's joinPosition differs from the container's recovered logPosition; mismatched consensus/log replication across the cluster; wrong snapshot or log position in recovery plan; reconnected to an image that started later than expected position.","commonSituations":"Mixed-version or misconfigured cluster members with different log positions; snapshot restore combined with a log whose recording starts at a different position; archive channel/stream configuration pointing to the wrong recordings.","solutions":["Verify all cluster members agree on the same snapshot and log position (check recovery plan / cluster-mark file)","Ensure consistent archive and consensus channel configuration across nodes","Restore from a snapshot whose recorded logPosition matches the available log; re-snapshot the cluster if necessary","Check recording log for gaps or a truncated recording and replay from a valid position"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"Image image = /* awaited image */;\nif (image.joinPosition() != recoveredLogPosition) {\n    throw new IllegalStateException(\"log not contiguous: expected \" + recoveredLogPosition + \" got \" + image.joinPosition());\n}","typeGuard":null,"tryCatchPattern":"try { container.recover(); } catch (ClusterException ex) { /* halt node; do not retry blindly — check recovery plan consistency */ }","preventionTips":["Keep all members on the same Aeron version","Ensure snapshot and log recordings are complete before restarting nodes","Monitor archive recording continuity across the cluster"],"tags":["aeron","cluster","consistency","log"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}