aeron-io/aeron · error · ClusterException

expected schemaId= , actual=

Error message

expected schemaId=${MessageHeaderDecoder.SCHEMA_ID}, actual=${schemaId}

What it means

ConsensusAdapter.onFragment wraps each incoming buffer with MessageHeaderDecoder and verifies the SBE block schemaId matches the cluster protocol's expected SCHEMA_ID; a mismatch throws ClusterException. This means a message arrived on the consensus transport that is not a valid cluster-protocol SBE message.

Solutions

  1. Verify all cluster members run the same Aeron version so their SBE schemas match.
  2. Check aeron.cluster.channels / consensus channel and streamId configuration for collisions with other applications.
  3. Ensure no other publisher is sending on the consensus channel/streamId.
  4. If connecting clusters together, use distinct channels or an egress/ingest boundary rather than sharing the consensus stream.

Example fix

// before
// node A: -Daeron.cluster.consensus.channel=aeron:udp?endpoint=host:9010 (shared with another app sending SBE with different schema)
// after: dedicate the stream/channel to the cluster
-Daeron.cluster.consensus.channel=aeron:udp?endpoint=host:9020
-Daeron.cluster.consensus.streamId=101
Defensive patterns

Strategy: validation

Validate before calling

// before joining a cluster, verify config consistency
if (!consensusChannel.equals(expectedConsensusChannel) || consensusStreamId != expectedStreamId)
{
    throw new IllegalStateException("consensus channel/streamId mismatch with cluster config");
}

Try / catch

try
{
    consensusModule.start();
}
catch (ClusterException ex)
{
    if (ex.getMessage().contains("expected schemaId"))
    {
        // wrong sender or Aeron version on the consensus channel
    }
    else throw ex;
}

Prevention

When it happens

Trigger: Receiving a fragment on the cluster consensus channel whose SBE header schemaId differs from MessageHeaderDecoder.SCHEMA_ID (e.g. messages from another Aeron/SBE protocol version or a different application sharing the channel/stream).

Common situations: Two Aeron versions or applications configured to use the same channel/stream: one speaks a different SBE schema; misconfigured consensus channel overlapping with another cluster's streamId; corrupted messages from a misbehaving publisher.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusAdapter.java:87

    public int poll()
    {
        return subscription.poll(fragmentAssembler, FRAGMENT_LIMIT);
    }

    public int poll(final int limit)
    {
        return subscription.poll(fragmentAssembler, limit);
    }

    @SuppressWarnings("MethodLength")
    public void onFragment(final DirectBuffer buffer, final int offset, final int length, final Header header)
    {
        messageHeaderDecoder.wrap(buffer, offset);

        final int schemaId = messageHeaderDecoder.schemaId();
        if (schemaId != MessageHeaderDecoder.SCHEMA_ID)
        {
            throw new ClusterException("expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + ", actual=" + schemaId);
        }

        switch (messageHeaderDecoder.templateId())
        {
            case CanvassPositionDecoder.TEMPLATE_ID:
                canvassPositionDecoder.wrap(
                    buffer,
                    offset + MessageHeaderDecoder.ENCODED_LENGTH,
                    messageHeaderDecoder.blockLength(),
                    messageHeaderDecoder.version());

                consensusModuleAgent.onCanvassPosition(
                    canvassPositionDecoder.logLeadershipTermId(),
                    canvassPositionDecoder.logPosition(),
                    canvassPositionDecoder.leadershipTermId(),
                    canvassPositionDecoder.followerMemberId(),
                    canvassPositionDecoder.protocolVersion());
                break;

View on GitHub (pinned to 6d60124e15)