aeron-io/aeron · error · ClusterException
expected schemaId= , actual=
Error message
expected schemaId=${MessageHeaderDecoder.SCHEMA_ID}, actual=${schemaId} What it means
ServiceAdapter.onFragment decodes each incoming SBE message beginning with a MessageHeaderDecoder and validates the schemaId against the cluster protocol schema. A mismatch means the fragment is not encoded with the expected cluster-service protocol schema, so it throws this ClusterException rather than misinterpreting the buffer.
Solutions
- Run matching Aeron/artifact versions across all cluster nodes (all nodes built from the same aeron-cluster version).
- Verify the service is subscribed on the correct consensus module stream and streamId.
- Check for corruption or stale data in the cluster/mark file and log directories; clean and re-form the cluster.
- Confirm no custom publisher is writing non-cluster messages to this image.
Example fix
// before // service built against io.aeron:aeron-cluster:1.40 mixing with 1.44 nodes // after // align all nodes to the same version, e.g.: // implementation 'io.aeron:aeron-cluster:1.44.1' in every module
Defensive patterns
Strategy: validation
Validate before calling
// before subscribing/decoding, verify sender version compatibility: // ensure all nodes use the same aeron-cluster artifact version generating the SBE schema
Try / catch
try {
serviceAdapter.onFragment(buffer, offset, length, header);
} catch (ClusterException e) {
if (e.getMessage().contains("expected schemaId=")) {
// halt and alert: protocol/version mismatch in cluster
} else {
throw e;
}
} Prevention
- Pin identical Aeron versions across all cluster nodes.
- Never publish non-cluster SBE messages on the consensus-module-to-service stream.
- Clean stale cluster/log directories after failed runs or upgrades.
When it happens
Trigger: A fragment published on the service adapter's (consensus module -> service) stream whose SBE header schemaId differs from cluster-cluster MessageHeaderDecoder.SCHEMA_ID — e.g. foreign/corrupt data on the channel or a version-incompatible sender.
Common situations: Mixed Aeron versions in one cluster (different generated schema ids); custom protocol extensions sending on the wrong stream; replay/corrupt log data fed to the adapter; connecting a service to the wrong channel/stream-id.
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
- expected schemaId= , actual=
- expected schemaId= , actual=
- expected schemaId=<MessageHeaderDecoder.SCHEMA_ID>, actual=
- expected schemaId=<MessageHeaderDecoder.SCHEMA_ID>, actual=
- expected schemaId=<MessageHeaderDecoder.SCHEMA_ID>, actual=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/1dd736b62723f2e8.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/ServiceAdapter.java:63
public void close()
{
CloseHelper.close(subscription);
}
int poll()
{
return subscription.poll(fragmentAssembler, FRAGMENT_LIMIT);
}
private 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 JoinLogDecoder.TEMPLATE_ID:
joinLogDecoder.wrap(
buffer,
offset + MessageHeaderDecoder.ENCODED_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
clusteredServiceAgent.onJoinLog(
joinLogDecoder.logPosition(),
joinLogDecoder.maxLogPosition(),
joinLogDecoder.memberId(),
joinLogDecoder.logSessionId(),
joinLogDecoder.logStreamId(),
joinLogDecoder.isStartup() == BooleanType.TRUE,View on GitHub (pinned to 6d60124e15)