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

  1. Use matching aeron-archive versions on both ends so schema ids agree
  2. Isolate the control-response stream from non-archive publishers
  3. Check the recorded schemaId in the exception against your codec's MessageHeaderDecoder.SCHEMA_ID
  4. 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

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


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)