aeron-io/aeron · warning · ClusterException

joinPosition= expected startPosition=

Error message

joinPosition=${joinPosition} expected startPosition=${startPosition}

What it means

During log replay, LogReplay.doWork locates the replay Image by session id and asserts its joinPosition equals the expected startPosition for the replay. If the image was joined at a different position the replay would deliver wrong entries, so a WARN-category ClusterException is thrown and the replay fails.

Solutions

  1. Verify the archive contains a recording covering the requested replay startPosition (aeron-archive catalog)
  2. Restore from a later snapshot so replay can start from a position the recording actually contains
  3. Ensure the log subscription is bound to the correct log session id and no stale image is reused
  4. Check archive recording start positions vs the cluster's recovery plan (recoveryPlan)

Example fix

// before: replaying from an arbitrary snapshot position
replay(fromPosition);
// after: use the recovery plan's snapshot/recording positions
RecoveryPlan plan = archiveClient.buildRecoveryPlan(...);
replay(plan.snapshotPosition());
Defensive patterns

Strategy: try-catch

Validate before calling

if (image.joinPosition() != replayStartPosition) { /* re-plan recovery from snapshot */ }

Try / catch

try { replay.doWork(); } catch (ClusterException e) { /* WARN category: fall back to a later snapshot */ }

Prevention

When it happens

Trigger: Replaying the log after election/snapshot recovery when the archive image for the log session was joined at a position that differs from the replay startPosition — e.g. the archive recording starts later than the requested replay position, or a stale subscription image is picked up.

Common situations: Missing or truncated archive segments so the recording's start position moved forward; racing a new recording image while replaying; misconfigured replay channel/merge in cluster recovery.

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/43853299456b4ae1. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/LogReplay.java:88

        {
            errorHandler.onError(ex);
        }
        logAdapter.disconnect(errorHandler);
        CloseHelper.close(errorHandler, logSubscription);
    }

    int doWork()
    {
        int workCount = 0;

        if (null == logAdapter.image())
        {
            final Image image = logSubscription.imageBySessionId(logSessionId);
            if (null != image)
            {
                if (image.joinPosition() != startPosition)
                {
                    throw new ClusterException(
                        "joinPosition=" + image.joinPosition() + " expected startPosition=" + startPosition,
                        ClusterException.Category.WARN);
                }

                logAdapter.image(image);
                consensusModuleAgent.awaitServicesReady(
                    logSubscription.channel(),
                    logSubscription.streamId(),
                    logSessionId,
                    startPosition,
                    stopPosition,
                    true,
                    Cluster.Role.FOLLOWER);

                workCount += 1;
            }
        }
        else

View on GitHub (pinned to 6d60124e15)