aeron-io/aeron · error · ClusterException

clusterMembers and endpoints differ

Error message

clusterMembers and endpoints differ: ${member.endpoints} != ${memberEndpoints}

What it means

ClusterMember.validateMemberEndpoints compares the endpoints parsed from a member's entry in the clusterMembers config string against the memberEndpoints string supplied for that member. If they differ, it throws ClusterException. This guards against a member whose configured membership list and its own advertised endpoints disagree, which would break cluster connectivity.

Solutions

  1. Compare the member's entry in the clusterMembers list with aeron.cluster.member.endpoints and make them byte-for-byte identical (same endpoints in same order).
  2. Check for stale ports/hostnames after a migration and update both properties together.
  3. If generated by a tool, fix the generator so both values come from a single source of truth.
  4. Restart the node after correcting the configuration.

Example fix

// before
-Daeron.cluster.members=0,hostA:20000,hostA:20001,hostA:20002,hostA:20003,hostA:20004|1,hostB:20000,...
-Daeron.cluster.member.endpoints=hostA:20000,hostA:20001,hostA:20003,hostA:20004
// after (member-level endpoints must match the clusterMembers entry exactly)
-Daeron.cluster.member.endpoints=hostA:20000,hostA:20001,hostA:20002,hostA:20003,hostA:20004
Defensive patterns

Strategy: validation

Validate before calling

// before starting the node
ClusterMember[] members = ClusterMember.parse(0, memberEndpointsConfigString);
ClusterMember thisMember = ClusterMember.findMember(members, ClusterMember.parse(NULL_VALUE, memberEndpoints));
if (thisMember == null) {
    throw new IllegalArgumentException("member endpoints not found in clusterMembers list");
}

Prevention

When it happens

Trigger: Calling ClusterMember.validateMemberEndpoints(member, memberEndpoints) where member.endpoints (parsed from the clusterMembers string) does not exactly equal the parsed memberEndpoints string for that member (mismatched client-facing, member-facing, log, archive or replication endpoints).

Common situations: Startup validation of a cluster member where the clusterMembers system property and the member's own aeron.cluster.member.endpoints were edited independently; a copy-paste error leaving an old port or hostname in one of the two; automated config generation producing inconsistent values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ClusterMember.java:1085

        return member;
    }

    /**
     * Check the member with the memberEndpoints.
     *
     * @param member          to check memberEndpoints against.
     * @param memberEndpoints to check member against.
     * @see ConsensusModule.Context#memberEndpoints()
     * @see ConsensusModule.Context#clusterMembers()
     */
    public static void validateMemberEndpoints(final ClusterMember member, final String memberEndpoints)
    {
        final ClusterMember endpoints = ClusterMember.parseEndpoints(NULL_VALUE, memberEndpoints);

        if (!areSameEndpoints(member, endpoints))
        {
            throw new ClusterException(
                "clusterMembers and endpoints differ: " + member.endpoints + " != " + memberEndpoints);
        }
    }

    /**
     * Are two cluster members using the same endpoints?
     *
     * @param lhs to compare for equality.
     * @param rhs to compare for equality.
     * @return true if both are using the same endpoints or false if not.
     */
    public static boolean areSameEndpoints(final ClusterMember lhs, final ClusterMember rhs)
    {
        return lhs.ingressEndpoint.equals(rhs.ingressEndpoint) &&
            lhs.consensusEndpoint.equals(rhs.consensusEndpoint) &&
            lhs.logEndpoint.equals(rhs.logEndpoint) &&
            lhs.catchupEndpoint.equals(rhs.catchupEndpoint) &&
            lhs.archiveEndpoint.equals(rhs.archiveEndpoint);

View on GitHub (pinned to 6d60124e15)