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

  1. Run the same aeron-archive version on both the client and the archive media driver so the SBE schema ids match.
  2. Verify the subscription is bound to the actual archive control response channel/stream, not an unrelated image.
  3. Inspect the first bytes of the incoming fragment to confirm the peer is speaking the Aeron archive SBE protocol.
  4. 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

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)