aeron-io/aeron · error · ArchiveException

expected schemaId=

Error message

expected schemaId=

What it means

RecordingEventsAdapter.onFragment validates the SBE MessageHeader schemaId of each recording-event fragment against MessageHeaderDecoder.SCHEMA_ID. If the event stream delivers messages from a different schema (wrong channel content, version skew between client and archive), it throws ArchiveException('expected schemaId=..., actual=...').

Solutions

  1. Upgrade/align aeron-archive client and archive agent to the same version.
  2. Dedicate the recording events channel/stream to archive events only.
  3. Check ChannelUri streamId used for the events subscription matches the archive driver's configuration.

Example fix

// before
// events subscription pointing at a shared app stream
AeronArchive.addRecordingEventsPoller(eventsChannel, wrongStreamId);

// after
// set the archive driver's recording events channel and matching stream id
// -Daeron.archive.recording.events.channel=aeron:udp?endpoint=localhost:8011
// subscribe with the same streamId the agent publishes on
Defensive patterns

Strategy: try-catch

Try / catch

try {
    recordingEventsAdapter.poll();
} catch (ArchiveException e) {
    if (e.getMessage().startsWith("expected schemaId=")) {
        // resubscribe to the correct events channel or upgrade the archive
    }
}

Prevention

When it happens

Trigger: A fragment on the recording events subscription has a header schemaId != MessageHeaderDecoder.SCHEMA_ID — e.g. the events channel/stream receives data from an archive running a different SBE schema, or a non-archive publisher shares the stream.

Common situations: Recording events listener attached to a stream also used by other applications; client/server archive version mismatch after an upgrade; misconfigured RecordingEventsPoller/adapter wired to the wrong endpoint.

Related errors


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

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/RecordingEventsAdapter.java:77

     * @return the number of fragments read during the operation. Zero if no events are available.
     */
    public int poll()
    {
        return subscription.poll(this, fragmentLimit);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public 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 RecordingStartedDecoder.TEMPLATE_ID:
                recordingStartedDecoder.wrap(
                    buffer,
                    offset + MessageHeaderDecoder.ENCODED_LENGTH,
                    messageHeaderDecoder.blockLength(),
                    messageHeaderDecoder.version());

                listener.onStart(
                    recordingStartedDecoder.recordingId(),
                    recordingStartedDecoder.startPosition(),
                    recordingStartedDecoder.sessionId(),
                    recordingStartedDecoder.streamId(),
                    recordingStartedDecoder.channel(),
                    recordingStartedDecoder.sourceIdentity());

View on GitHub (pinned to 6d60124e15)