aeron-io/aeron · error · ArchiveException

expected schemaId=

Error message

expected schemaId=

What it means

RecordingDescriptorPoller.onFragment wraps the incoming SBE buffer in a MessageHeaderDecoder and verifies the SBE schemaId matches the archive protocol schema. A mismatch means the fragment is not an archive control-protocol message (wrong channel, corrupted frame, or a different SBE schema version), so ArchiveException('expected schemaId=..., actual=...') is thrown.

Solutions

  1. Use matching Aeron Archive client and server versions (same SBE schema).
  2. Point the poller's subscription at the archive control response channel/stream it was designed for.
  3. Verify the stream is not shared with other publishers sending non-archive messages.

Example fix

// before
// poller subscribed to a stream fed by a different Aeron Archive version
archive.listRecording(subscription, poller, recordingId); // ArchiveException: expected schemaId=...

// after
// align versions in pom.xml
// <dependency><artifactId>aeron-archive</artifactId><version>X.Y.Z</version></dependency>
// <dependency><artifactId>aeron-driver</artifactId><version>X.Y.Z</version></dependency>
Defensive patterns

Strategy: try-catch

Try / catch

try {
    archive.listRecording(recordingId, recordingDescriptorPoller);
} catch (ArchiveException e) {
    if (e.getMessage().startsWith("expected schemaId=")) {
        // wrong stream content or schema version mismatch: reconnect/upgrade
    }
}

Prevention

When it happens

Trigger: A fragment arrives on the subscription used by RecordingDescriptorPoller whose header schemaId differs from MessageHeaderDecoder.SCHEMA_ID — e.g. the poller was pointed at a channel carrying non-archive data, or client/server archive SBE schema versions differ.

Common situations: Subscribing the descriptor poller to the wrong stream/channel; mixing Aeron client and archive versions (protocol schema changed between releases); garbage from a misconfigured shared endpoint.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/a80b3e61152e74bd. Report an issue: GitHub.

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/RecordingDescriptorPoller.java:176

        this.remainingRecordCount = recordCount;
        isDispatchComplete = false;
    }

    @SuppressWarnings("MethodLength")
    ControlledFragmentAssembler.Action onFragment(
        final DirectBuffer buffer, final int offset, final int length, final Header header)
    {
        if (isDispatchComplete)
        {
            return ControlledFragmentAssembler.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);
        }

        final int templateId = messageHeaderDecoder.templateId();
        switch (templateId)
        {
            case ControlResponseDecoder.TEMPLATE_ID:
                controlResponseDecoder.wrap(
                    buffer,
                    offset + MessageHeaderEncoder.ENCODED_LENGTH,
                    messageHeaderDecoder.blockLength(),
                    messageHeaderDecoder.version());

                if (controlResponseDecoder.controlSessionId() == controlSessionId)
                {
                    final ControlResponseCode code = controlResponseDecoder.code();
                    final long responseCorrelationId = controlResponseDecoder.correlationId();

                    if (ControlResponseCode.RECORDING_UNKNOWN == code && responseCorrelationId == correlationId)

View on GitHub (pinned to 6d60124e15)