aeron-io/aeron · error · InvalidChannelException

option conflicts with existing subscription: isResponse=

Error message

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

What it means

The 'response' option on Aeron subscriptions must be consistent across all subscriptions matching the same channel endpoint and streamId. This InvalidChannelException is thrown when a new subscription matches an existing one but sets isResponse to a different value.

Solutions

  1. Set the same response value on all subscriptions for that channel/streamId
  2. Close the conflicting existing subscription before re-adding with the new option
  3. Use a separate streamId or channel endpoint for response subscriptions
  4. Keep a single URI builder so option parity is guaranteed

Example fix

// before
aeron.addSubscription("aeron:udp?endpoint=localhost:40456", 1001); // existing, response=false
// after
aeron.addSubscription("aeron:udp?endpoint=localhost:40456|response=true", 1001); // now consistent
Defensive patterns

Strategy: try-catch

Validate before calling

boolean response = channel.contains("response=true");
// ensure consistency with all other subscriptions on this channel/streamId

Try / catch

try { aeron.addSubscription(channel, streamId, handler, unavailableHandler); } catch (InvalidChannelException e) { if (e.getMessage().contains("isResponse=")) { log.error("response option mismatch: {}", e.getMessage()); } throw e; }

Prevention

When it happens

Trigger: Adding a subscription with response=true (response channels) on a channel/streamId where a subscription with response=false (or default) already exists and matches tag/endpoint/stream, e.g. mixing "aeron:udp?endpoint=h:p|response=true" with a plain subscription.

Common situations: Enabling the response-channel feature on one subscriber while legacy subscribers on the same stream omit it; component adds a response subscription to a shared stream; copy-pasted URIs where one gained the response param.

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

Appendix: source

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

                    if (params.isReliable != subscription.isReliable())
                    {
                        throw new InvalidChannelException(
                            "option conflicts with existing subscription: reliable=" + params.isReliable +
                                " existingChannel=" + subscription.channel() + " channel=" +
                                udpChannel.originalUriString());
                    }

                    if (params.isRejoin != subscription.isRejoin())
                    {
                        throw new InvalidChannelException(
                            "option conflicts with existing subscription: rejoin=" + params.isRejoin +
                                " existingChannel=" + subscription.channel() + " channel=" +
                                udpChannel.originalUriString());
                    }

                    if (params.isResponse != subscription.isResponse())
                    {
                        throw new InvalidChannelException(
                            "option conflicts with existing subscription: isResponse=" + params.isResponse +
                                " existingChannel=" + subscription.channel() + " channel=" +
                                udpChannel.originalUriString());
                    }
                }
            }
        }
    }

    private void linkMatchingImages(final SubscriptionLink subscriptionLink)
    {
        for (int i = 0, size = publicationImages.size(); i < size; i++)
        {
            final PublicationImage image = publicationImages.get(i);
            if (subscriptionLink.matches(image) && image.isAcceptingSubscriptions())
            {
                final long registrationId = subscriptionLink.registrationId();
                final long joinPosition = image.joinPosition();

View on GitHub (pinned to 6d60124e15)