aeron-io/aeron · warning · TimeoutException

failed to join live log as follower

Error message

failed to join live log as follower

What it means

TimeoutException (WARN) thrown when a follower subscribed to the new live log during election but did not connect/join it within ctx.leaderHeartbeatTimeoutNs. The subscription exists but no image connected before the deadline, so the follower cannot transition to FOLLOWER_READY and the election fails for this member.

Solutions

  1. Verify the leader's log publication endpoint is reachable from the follower (check log channel config and firewall)
  2. Increase ctx.leaderHeartbeatTimeoutNs()
  3. Check media driver logs on both sides for subscription/publication connection errors
  4. Confirm the leader is still the leader; restart the member to redo the election

Example fix

// before
ctx.leaderHeartbeatTimeoutNs(TimeUnit.SECONDS.toNanos(10));
// after
ctx.leaderHeartbeatTimeoutNs(TimeUnit.SECONDS.toNanos(30));
Defensive patterns

Strategy: retry

Try / catch

catch (TimeoutException e) {
    // live log join timed out; election will retry; alert if repeated
}

Prevention

When it happens

Trigger: In FOLLOWER_LIVE_LOG_AWAIT (after adding the live log subscription): if the log image never becomes ready before timeOfLastStateChangeNs + ctx.leaderHeartbeatTimeoutNs(), this is thrown.

Common situations: Leader's log publication not connected back to the follower (network/firewall), leader changed or died during election, slow driver, leaderHeartbeatTimeoutNs too small for clusters with many members.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/abb5fe2082f5cef9. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/Election.java:1150

    }

    private int followerLogAwait(final long nowNs)
    {
        int workCount = 0;

        final Image image = logSubscription.imageBySessionId(logSessionId);
        if (null != image)
        {
            verifyLogJoinPosition("followerLogAwait", image.joinPosition());
            if (consensusModuleAgent.tryJoinLogAsFollower(image, isLeaderStartup, nowNs))
            {
                updateRecordingLog(nowNs);
                state(FOLLOWER_READY, nowNs, "");
                workCount++;
            }
            else if (nowNs >= (timeOfLastStateChangeNs + ctx.leaderHeartbeatTimeoutNs()))
            {
                throw new TimeoutException("failed to join live log as follower", AeronException.Category.WARN);
            }
        }
        else if (ChannelEndpointStatus.ERRORED == logSubscription.channelStatus())
        {
            final String message = "failed to add live log as follower - " + logSubscription.channel();
            throw new ClusterException(message, AeronException.Category.WARN);
        }
        else if (nowNs >= (timeOfLastStateChangeNs + ctx.leaderHeartbeatTimeoutNs()))
        {
            throw new TimeoutException("failed to join live log", AeronException.Category.WARN);
        }

        return workCount;
    }

    private int followerReady(final long nowNs)
    {
        if (consensusPublisher.appendPosition(

View on GitHub (pinned to 6d60124e15)