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

  1. Verify all cluster members agree on the same snapshot and log position (check recovery plan / cluster-mark file)
  2. Ensure consistent archive and consensus channel configuration across nodes
  3. Restore from a snapshot whose recorded logPosition matches the available log; re-snapshot the cluster if necessary
  4. 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

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


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)