aeron-io/aeron · critical · ClusterException

Extension subscription joinPosition

Error message

Extension subscription joinPosition (<joinPosition>) does not match logPosition (<logPosition>)

What it means

When a follower becomes leader it attaches an extension subscription to receive the remainder of the log from the previous leader. The image's joinPosition must equal the node's current logPosition; if not, the log continuation would leave a gap or overlap, so the agent throws rather than corrupting the log.

Solutions

  1. Ensure the node replays logs fully from the archive before attempting leadership extension.
  2. Check for split-brain: verify quorum and remove any falsely-elected leader.
  3. Confirm archive/log channel configurations are identical across all cluster nodes.
  4. Restart the node from a consistent snapshot so its logPosition aligns with the cluster.

Example fix

// before: election triggered while replay incomplete
// after: wait for replay to finish before offering leadership
while (replayPosition < stopPosition) { agent.doWork(); }
Defensive patterns

Strategy: validation

Validate before calling

// after replay, before election completes
long joinPos = subscription.imageAtIndex(0).joinPosition();
if (joinPos != consensusModule.logPosition()) { rejoinElection(); }

Try / catch

try { agent.doWork(); } catch (ClusterException e) { if (e.getMessage().contains("joinPosition") && e.getMessage().contains("logPosition")) { triggerElectionRestart(); } else { throw e; } }

Prevention

When it happens

Trigger: During leader election/extension, subscription.imageAtIndex(0).joinPosition() differs from the local logPosition — the incoming image starts at a different position than where this node's replay ended.

Common situations: Split-brain or archived-log truncation leaving nodes at divergent positions; archive replay ended at a different position than the new leader is streaming from; misconfigured archive/log channels causing the wrong image to be joined.

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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModuleAgent.java:1732

        connectLeaderLogSubscriptionForExtension(logPosition);
    }

    private void connectLeaderLogSubscriptionForExtension(final long logPosition)
    {
        if (null != consensusModuleExtension)
        {
            final Subscription subscription = aeron.addSubscription(localLogChannel, ctx.logStreamId());

            idleStrategy.reset();
            while (0 == subscription.imageCount())
            {
                idle();
            }

            final long joinPosition = subscription.imageAtIndex(0).joinPosition();
            if (joinPosition != logPosition)
            {
                throw new ClusterException(
                    "Extension subscription " +
                    "joinPosition (" + joinPosition + ") does not match logPosition (" + logPosition + ")");
            }

            this.extensionLeaderSubscription = subscription;
        }
    }

    void liveLogDestination(final String liveLogDestination)
    {
        this.liveLogDestination = liveLogDestination;
    }

    String liveLogDestination()
    {
        return liveLogDestination;
    }

View on GitHub (pinned to 6d60124e15)