aeron-io/aeron · error · ClusterException

invalid clusterSessionId= expected=

Error message

invalid clusterSessionId= expected=

What it means

onNewLeader receives a NewLeaderEvent from the cluster and validates that the event's clusterSessionId matches the session id this client established. A mismatch means the event belongs to a different session (stale or cross-wired egress image), so the library throws ClusterException to prevent the client from adopting the wrong leader session state.

Solutions

  1. Ensure each AeronCluster client has its own exclusive egress subscription; do not share egress images between clients or sessions.
  2. Drain/discard fragments from the previous session before processing events for a new connection.
  3. Reconnect the client (connect()) so clusterSessionId and egress image are established together.
  4. Catch ClusterException in egress handlers and log the session mismatch to diagnose subscription sharing.

Example fix

// before
sharedSubscription.poll(handler, 0); // handler.onNewLeader throws on stale session events
// after
if (newLeaderClusterSessionId == myClient.clusterSessionId()) { myClient.onNewLeader(...); } else { discardStaleFragment(); }
Defensive patterns

Strategy: validation

Validate before calling

if (eventClusterSessionId != client.clusterSessionId()) { log.warn("stale NewLeaderEvent ignored"); return; }

Try / catch

try { client.onNewLeader(leadershipTermId, leaderMemberId, ingressEndpoints); } catch (ClusterException e) { if (e.getMessage().startsWith("invalid clusterSessionId=")) { discardStaleEgressAndReconnect(); } }

Prevention

When it happens

Trigger: Calling onNewLeader() (via onFragment/onControlledFragment egress processing) when the incoming NewLeaderEvent's clusterSessionId differs from the client's clusterSessionId — e.g. egress from a previous session still queued during reconnect, or misconfigured egress subscription receiving another client's events.

Common situations: Reusing an egress Subscription or Image across AeronCluster reconnects; sharing a subscription between multiple cluster clients; processing stale buffered fragments after the old session was closed.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/client/AeronCluster.java:679

    /**
     * To be called when a new leader event is delivered. This method needs to be called when using the
     * {@link EgressAdapter} or {@link EgressPoller} rather than {@link #pollEgress()} method.
     *
     * @param clusterSessionId which must match {@link #clusterSessionId()}.
     * @param leadershipTermId that identifies the term for which the new leader has been elected.
     * @param leaderMemberId   which has become the new leader.
     * @param ingressEndpoints comma separated list of cluster ingress endpoints to connect to with the leader first.
     */
    public void onNewLeader(
        final long clusterSessionId,
        final long leadershipTermId,
        final int leaderMemberId,
        final String ingressEndpoints)
    {
        if (clusterSessionId != this.clusterSessionId)
        {
            throw new ClusterException(
                "invalid clusterSessionId=" + clusterSessionId + " expected=" + this.clusterSessionId);
        }

        state(State.AWAIT_NEW_LEADER_CONNECTION, nanoClock.nanoTime() + ctx.messageTimeoutNs());

        this.leadershipTermId = leadershipTermId;
        this.leaderMemberId = leaderMemberId;
        sessionMessageHeaderEncoder.leadershipTermId(leadershipTermId);

        CloseHelper.close(publication);
        if (null == ctx.ingressEndpoints())
        {
            publication = addNewLeaderIngressPublication(ctx, ctx.ingressChannel(), ctx.ingressStreamId());
        }
        else
        {
            ctx.ingressEndpoints(ingressEndpoints);
            updateMemberEndpoints(ingressEndpoints, leaderMemberId);

View on GitHub (pinned to 6d60124e15)