aeron-io/aeron · critical · ClusterException
Cluster log must be contiguous for joining image…
Error message
Cluster log must be contiguous for joining image: expectedPosition=<logPosition> joinPosition=<image.joinPosition()>
What it means
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.
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
Defensive patterns
Strategy: validation
Validate before calling
Image image = /* awaited image */;
if (image.joinPosition() != recoveredLogPosition) {
throw new IllegalStateException("log not contiguous: expected " + recoveredLogPosition + " got " + image.joinPosition());
} Try / catch
try { container.recover(); } catch (ClusterException ex) { /* halt node; do not retry blindly — check recovery plan consistency */ } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cluster log must be contiguous for active log event…
- active counter not found
- Aeron client must use a RethrowingErrorHandler
- already in snapshot
- claim exceeds maxPayloadLength=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/011a66f59da98ffa.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/ClusteredServiceAgent.java:865
if (Role.LEADER != activeLog.role)
{
disconnectEgress(ctx.countedErrorHandler());
}
this.standbySnapshotFlags = activeLog.isStandby ? CLUSTER_ACTION_FLAGS_STANDBY_SNAPSHOT :
CLUSTER_ACTION_FLAGS_DEFAULT;
final String channel = new ChannelUriStringBuilder(activeLog.channel)
.alias(subscriptionAlias)
.build();
Subscription logSubscription = aeron.addSubscription(channel, activeLog.streamId);
try
{
final Image image = awaitImage(activeLog.sessionId, logSubscription);
if (image.joinPosition() != logPosition)
{
throw new ClusterException("Cluster log must be contiguous for joining image: " +
"expectedPosition=" + logPosition + " joinPosition=" + image.joinPosition());
}
if (activeLog.logPosition != logPosition)
{
throw new ClusterException("Cluster log must be contiguous for active log event: " +
"expectedPosition=" + logPosition + " eventPosition=" + activeLog.logPosition);
}
logAdapter.image(image);
logAdapter.maxLogPosition(activeLog.maxLogPosition);
logSubscription = null;
final long id = ackId++;
while (!consensusModuleProxy.ack(activeLog.logPosition, clusterTime, id, NULL_VALUE, serviceId))
{
idle();
}View on GitHub (pinned to 6d60124e15)