aeron-io/aeron · error · ArchiveException

expected schemaId=

Error message

expected schemaId=

What it means

ControlResponseAdapter.onFragment decodes the SBE MessageHeader of each fragment on the control-response stream and throws ArchiveException if the schemaId is not the expected archive control protocol schema id. This guards the adapter against decoding non-archive-control payloads as control responses.

Solutions

  1. Upgrade/downgrade aeron-archive so client and server share one schema version
  2. Dedicate the control-response channel/stream to archive control traffic only
  3. Rebuild with a clean classpath so generated codecs match MessageHeaderDecoder.SCHEMA_ID
  4. Log the offending schemaId and templateId before rethrowing to identify the foreign publisher

Example fix

// before
subscription.handler(this::onFragment); // any stream traffic lands here

// after
// validate before invoking the adapter
if (header.schemaId() == MessageHeaderDecoder.SCHEMA_ID) {
    controlResponseAdapter.onFragment(buffer, offset, length, header);
}
Defensive patterns

Strategy: validation

Validate before calling

MessageHeaderDecoder h = new MessageHeaderDecoder().wrap(buffer, offset);
if (h.schemaId() != MessageHeaderDecoder.SCHEMA_ID) { return; // skip foreign fragment
}
controlResponseAdapter.onFragment(buffer, offset, length, header);

Type guard

boolean validSchema(UnsafeBuffer buf, int offset) {
    messageHeaderDecoder.wrap(buf, offset);
    return messageHeaderDecoder.schemaId() == MessageHeaderDecoder.SCHEMA_ID;
}

Try / catch

try {
    controlResponseAdapter.onFragment(buffer, offset, length, header);
} catch (ArchiveException e) {
    if (e.getMessage().startsWith("expected schemaId=")) {
        log.error("schema mismatch; client/server aeron-archive versions differ", e);
    }
}

Prevention

When it happens

Trigger: onFragment is handed a fragment whose header schemaId != MessageHeaderDecoder.SCHEMA_ID: a mismatched client/server SBE schema version, foreign messages on the control-response stream, or a manually constructed/malformed buffer fed to the adapter.

Common situations: Version skew between aeron-archive jars (client vs server), routing unrelated application traffic to the archive control-response stream, classpath contamination with codecs generated from an older schema.

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/d3c77cfbfd265c56. Report an issue: GitHub.

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/ControlResponseAdapter.java:124

            decoder.initialTermId(),
            decoder.segmentFileLength(),
            decoder.termBufferLength(),
            decoder.mtuLength(),
            decoder.sessionId(),
            decoder.streamId(),
            decoder.strippedChannel(),
            decoder.originalChannel(),
            decoder.sourceIdentity());
    }

    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 ArchiveException("expected schemaId=" + MessageHeaderDecoder.SCHEMA_ID + ", actual=" + schemaId);
        }

        switch (messageHeaderDecoder.templateId())
        {
            case ControlResponseDecoder.TEMPLATE_ID:
                handleControlResponse(controlResponseListener, buffer, offset);
                break;

            case RecordingDescriptorDecoder.TEMPLATE_ID:
                handleRecordingDescriptor(controlResponseListener, buffer, offset);
                break;

            case RecordingSignalEventDecoder.TEMPLATE_ID:
                handleRecordingSignal(recordingSignalConsumer, buffer, offset);
                break;
        }
    }

View on GitHub (pinned to 6d60124e15)