aeron-io/aeron · error · ArchiveException
expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + "…
Error message
expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + ", actual=" + schemaId
What it means
ControlSessionAdapter decodes SBE messages from the archive control-response image. Every SBE frame carries a schemaId in its message header; if it does not match the schema id the archive codecs were generated from, the bytes cannot be interpreted, so an ArchiveException is thrown before decoding the templateId.
Solutions
- Run the same aeron-archive version on both the client and the archive media driver so the SBE schema ids match.
- Verify the subscription is bound to the actual archive control response channel/stream, not an unrelated image.
- Inspect the first bytes of the incoming fragment to confirm the peer is speaking the Aeron archive SBE protocol.
- If integrating a custom peer, regenerate codecs with the same SBE schema (aeron-archive-codecs) used by this build.
Example fix
// before: mixing versions
implementation("io.aeron:aeron-archive:1.40.0")
implementation("io.aeron:aeron-driver:1.44.1")
// after: aligned versions
implementation("io.aeron:aeron-archive:1.44.1")
implementation("io.aeron:aeron-driver:1.44.1") Defensive patterns
Strategy: validation
Validate before calling
// client-side: pin aeron-archive versions
assert io.aeron.archive.ClientContext.class.getPackage()
.getImplementationVersion().equals(driverArchiveVersion); Try / catch
try { adapter.onFragment(buffer, offset, length, header); }
catch (ArchiveException e) {
if (e.getMessage().contains("expected schemaId")) {
log.error("Archive protocol version mismatch; restart with matched versions", e);
}
} Prevention
- Keep client and archive dependencies on the exact same Aeron version.
- Subscribe only to genuine archive control response streams.
- Add an integration smoke test that connects client and archive before deploying.
When it happens
Trigger: onFragment receives a buffer whose MessageHeaderDecoder.schemaId() != MessageHeaderDecoder.SCHEMA_ID — e.g. a control response encoded with a different archive protocol version (different aeron-archive version on client vs server) or garbage/foreign protocol bytes arriving on the control channel.
Common situations: Client and archive agent running different Aeron versions with incompatible SBE schemas; sending non-archive protocol frames to the control request/response channel; connecting to a proxy/misconfigured endpoint that is not the archive control response stream.
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/2ed1f3417ab49506.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ControlSessionAdapter.java:136
return fragmentsRead;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("MethodLength")
public void onFragment(final DirectBuffer buffer, final int offset, final int length, final Header header)
{
ArchiveTracing.traceControlRequest(buffer, offset, length);
final MessageHeaderDecoder headerDecoder = decoders.header;
headerDecoder.wrap(buffer, offset);
final int schemaId = headerDecoder.schemaId();
if (schemaId != MessageHeaderDecoder.SCHEMA_ID)
{
throw new ArchiveException("expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + ", actual=" + schemaId);
}
final Image image = (Image)header.context();
final int templateId = headerDecoder.templateId();
switch (templateId)
{
case CloseSessionRequestDecoder.TEMPLATE_ID:
{
final CloseSessionRequestDecoder decoder = decoders.closeSessionRequest;
decoder.wrap(
buffer,
offset + MessageHeaderDecoder.ENCODED_LENGTH,
headerDecoder.blockLength(),
headerDecoder.version());
final long controlSessionId = decoder.controlSessionId();
final SessionInfo info = controlSessionByIdMap.get(controlSessionId);View on GitHub (pinned to 6d60124e15)