aeron-io/aeron · error · InvalidChannelException

option conflicts with existing subscription

Error message

option conflicts with existing subscription: ${CHANNEL_RECEIVE_TIMESTAMP_OFFSET_PARAM_NAME}=${offset} existingChannel=${existingChannel} channel=${channel}

What it means

Analogous to the send-timestamp check: when a new subscription maps to an existing ReceiveChannelEndpoint, its channel-receive-timestamp-offset param must equal that of the existing endpoint's subscription UDP channel. A mismatch is rejected with InvalidChannelException to keep receive timestamping consistent for all subscriptions sharing the endpoint.

Solutions

  1. Align channel-receive-timestamp-offset across all subscriptions sharing the channel
  2. Specify the param explicitly and identically in every channel URI string
  3. If different offsets are needed, use a different channel/endpoint for that subscription
  4. Catch InvalidChannelException on addSubscription and report the conflicting option to operators

Example fix

// before
aeron.addSubscription("aeron:udp?endpoint=224.0.1.1:40456|channel-receive-timestamp-offset=reserved", streamId);
aeron.addSubscription("aeron:udp?endpoint=224.0.1.1:40456", streamId); // mismatch -> InvalidChannelException
// after
String ch = "aeron:udp?endpoint=224.0.1.1:40456|channel-receive-timestamp-offset=reserved";
aeron.addSubscription(ch, streamId);
aeron.addSubscription(ch, streamId);
Defensive patterns

Strategy: validation

Validate before calling

ChannelUri uri = ChannelUri.parse(channel);
String offset = uri.get(CHANNEL_RECEIVE_TIMESTAMP_OFFSET_PARAM_NAME); // must match the existing subscription's value on the receive endpoint
if (existingOffset != null && !Objects.equals(existingOffset, offset))
{
    throw new IllegalArgumentException("channel-receive-timestamp-offset conflicts with existing subscription");
}

Try / catch

try
{
    aeron.addSubscription(channel, streamId);
}
catch (InvalidChannelException ex)
{
    if (ex.getMessage().contains(CHANNEL_RECEIVE_TIMESTAMP_OFFSET_PARAM_NAME))
    {
        log.error("Receive timestamp offset conflicts with an existing subscription on this channel");
    }
}

Prevention

When it happens

Trigger: Adding a subscription on the same UDP receive channel (e.g. same multicast group/endpoint) with a different channel-receive-timestamp-offset value than the endpoint's existing subscription; omitting the param when the first subscription set it (or vice versa).

Common situations: Mixed configuration across components in the same JVM subscribing to the same multicast feed; default changes between Aeron versions causing old and new channel strings to disagree; manual per-subscription timestamp tuning.

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/fbc52dc5586545f6. Report an issue: GitHub.

Appendix: source

Thrown at aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java:1537

        final UdpChannel udpChannel, final SendChannelEndpoint channelEndpoint)
    {
        if (udpChannel.channelSendTimestampOffset() != channelEndpoint.udpChannel().channelSendTimestampOffset())
        {
            throw new InvalidChannelException(
                "option conflicts with existing subscription: " + CHANNEL_SEND_TIMESTAMP_OFFSET_PARAM_NAME + "=" +
                    udpChannel.channelSendTimestampOffset() +
                    " existingChannel=" + channelEndpoint.originalUriString() + " channel=" +
                    udpChannel.originalUriString());
        }
    }

    private static void validateReceiveTimestampOffset(
        final UdpChannel udpChannel, final ReceiveChannelEndpoint channelEndpoint)
    {
        if (udpChannel.channelReceiveTimestampOffset() !=
            channelEndpoint.subscriptionUdpChannel().channelReceiveTimestampOffset())
        {
            throw new InvalidChannelException(
                "option conflicts with existing subscription: " + CHANNEL_RECEIVE_TIMESTAMP_OFFSET_PARAM_NAME + "=" +
                    udpChannel.channelReceiveTimestampOffset() +
                    " existingChannel=" + channelEndpoint.originalUriString() + " channel=" +
                    udpChannel.originalUriString());
        }
    }

    private SendChannelEndpoint findExistingManualSendChannelEndpoint(final long registrationId)
    {
        SendChannelEndpoint sendChannelEndpoint = null;

        for (final NetworkPublication publication : networkPublications)
        {
            if (registrationId == publication.registrationId())
            {
                sendChannelEndpoint = publication.channelEndpoint();
                break;
            }

View on GitHub (pinned to 6d60124e15)