aeron-io/aeron · error · ArchiveException
expected schemaId=
Error message
expected schemaId=
What it means
ControlResponsePoller.onFragment wraps each polled fragment with MessageHeaderDecoder and throws ArchiveException when the schemaId is not the archive control protocol's schema id. The poller only understands archive control responses, so fragments with a different schema are rejected rather than misinterpreted.
Solutions
- Use matching aeron-archive versions on both ends so schema ids agree
- Isolate the control-response stream from non-archive publishers
- Check the recorded schemaId in the exception against your codec's MessageHeaderDecoder.SCHEMA_ID
- Regenerate codecs from the correct SBE schema if you maintain a fork
Example fix
// before
poller.poll(); // throws on any foreign-schema fragment
// after
if (poller.poll() > 0 && !poller.isPollComplete()) {
// handle only after verifying schema inside your own wrapper
} Defensive patterns
Strategy: validation
Validate before calling
messageHeaderDecoder.wrap(buffer, offset);
if (messageHeaderDecoder.schemaId() != MessageHeaderDecoder.SCHEMA_ID) {
log.warn("skipping foreign fragment, schemaId=" + messageHeaderDecoder.schemaId());
return;
} Type guard
boolean isControlFragment(DirectBuffer buffer, int offset) {
messageHeaderDecoder.wrap(buffer, offset);
return messageHeaderDecoder.schemaId() == MessageHeaderDecoder.SCHEMA_ID;
} Try / catch
try {
poller.poll();
} catch (ArchiveException e) {
if (e.getMessage().startsWith("expected schemaId=")) {
log.error("control poller got non-archive-schema fragment", e);
}
} Prevention
- Poll only streams dedicated to archive control responses
- Match aeron-archive versions across all services in the deployment
- Version-gate the protocol before adding new message types
- Include schemaId in startup diagnostics for the control channel
When it happens
Trigger: Polling a subscription whose fragments carry a MessageHeader schemaId other than MessageHeaderDecoder.SCHEMA_ID — typically foreign SBE messages on the polled stream or a protocol-version mismatch between the poller's codecs and the sender's.
Common situations: Sharing one recording-event/control stream between archive control messages and application messages; mixed aeron-archive versions in the same cluster; hand-rolled publishers using a custom SBE schema on the polled channel.
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=
- expected schemaId=
- expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + "…
- expected schemaId=
- expected schemaId=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/e8dc745d6217de3f.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/client/ControlResponsePoller.java:275
public byte[] encodedChallenge()
{
return encodedChallenge;
}
ControlledFragmentAssembler.Action onFragment(
final DirectBuffer buffer, final int offset, final int length, final Header header)
{
if (isPollComplete)
{
return ControlledFragmentHandler.Action.ABORT;
}
messageHeaderDecoder.wrap(buffer, offset);
final int schemaId = messageHeaderDecoder.schemaId();
if (schemaId != MessageHeaderDecoder.SCHEMA_ID)
{
throw new ArchiveException("expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + ", actual=" + schemaId);
}
templateId = messageHeaderDecoder.templateId();
switch (templateId)
{
case ControlResponseDecoder.TEMPLATE_ID:
{
controlResponseDecoder.wrap(
buffer,
offset + MessageHeaderEncoder.ENCODED_LENGTH,
messageHeaderDecoder.blockLength(),
messageHeaderDecoder.version());
controlSessionId = controlResponseDecoder.controlSessionId();
correlationId = controlResponseDecoder.correlationId();
relevantId = controlResponseDecoder.relevantId();
code = controlResponseDecoder.code();
version = controlResponseDecoder.version();View on GitHub (pinned to 6d60124e15)