aeron-io/aeron · error · ConfigurationException

Channel media type mismatch. When using…

Error message

Channel media type mismatch. When using `control-mode=response`, the `replayChannel` media type must match the media type for the archive control channel.

What it means

When the replayChannel URI includes control-mode=response, the replay channel doubles as an archive control-response channel. conclude() compares its media type (IPC vs network) against the archive control request channel; a mismatch (e.g. replay over UDP while archive control uses IPC) would make response subscriptions unusable, so a ConfigurationException is thrown.

Solutions

  1. Make the replayChannel transport match the archive control channel: if controlRequestChannel is udp, use aeron:udp in replayChannel (and vice versa).
  2. Remove control-mode=response from the replayChannel if you do not need the replay stream to carry archive control responses.
  3. Align aeronArchiveContext.controlRequestChannel() with your intended transport so both sides agree.

Example fix

// before
archiveCtx.controlRequestChannel("aeron:udp?endpoint=localhost:8010");
ctx.replayChannel("aeron:ipc?control-mode=response"); // ipc vs udp mismatch
ctx.conclude(); // throws: Channel media type mismatch

// after
archiveCtx.controlRequestChannel("aeron:udp?endpoint=localhost:8010");
ctx.replayChannel("aeron:udp?endpoint=localhost:10001|control-mode=response");
Defensive patterns

Strategy: validation

Validate before calling

ChannelUri replayUri = ChannelUri.parse(replayChannel);
if (replayUri.hasControlModeResponse() &&
    replayUri.isIpc() != ChannelUri(archiveCtx.controlRequestChannel()).isIpc()) {
    throw new IllegalArgumentException("replayChannel transport must match archive control channel when control-mode=response");
}

Try / catch

try {
    ctx.conclude();
} catch (ConfigurationException e) {
    // rebuild replayChannel URI with matching transport
}

Prevention

When it happens

Trigger: Setting replayChannel to a URI containing '|control-mode=response' whose transport (ipc vs udp/tcp) differs from aeronArchiveContext.controlRequestChannel(). E.g. replayChannel="aeron:ipc?control-mode=response" while archive control is aeron:udp?..., or vice versa.

Common situations: Copying a shared-memory (ipc) archive config into a network deployment (or the reverse); manually adding control-mode=response for streaming-connection replay but on a different transport; container/host boundary changes the archive control channel default.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/PersistentSubscription.java:1734

            {
                throw new ConfigurationException("invalid recordingId " + recordingId);
            }

            if (FROM_LIVE > startPosition)
            {
                throw new ConfigurationException("invalid startPosition " + startPosition);
            }

            final ChannelUri replayChannelUri = ChannelUri.parse(replayChannel);

            if (replayChannelUri.hasControlModeResponse())
            {
                final String controlRequestChannel = aeronArchiveContext.controlRequestChannel();
                if (null != controlRequestChannel &&
                    !replayChannelUri.isIpc() == ChannelUri.parse(controlRequestChannel).isIpc()
                )
                {
                    throw new ConfigurationException(
                        "Channel media type mismatch. " +
                            "When using `control-mode=response`, the `replayChannel` media type must match the media" +
                            " type for the archive control channel."
                    );
                }
            }

            replayChannelUri.put(CommonContext.REJOIN_PARAM_NAME, "false");

            replayChannel = replayChannelUri.toString();

            if (null == aeron)
            {
                final Aeron.Context aeronCtx = new Aeron.Context()
                    .clientName("PersistentSubscription")
                    .subscriberErrorHandler(RethrowingErrorHandler.INSTANCE)
                    .useConductorAgentInvoker(true);
                if (null != aeronDirectoryName)

View on GitHub (pinned to 6d60124e15)