aeron-io/aeron · error · IllegalArgumentException

matching tag= has mismatched control-mode: <>

Error message

matching tag= has mismatched control-mode:  <> 

What it means

Thrown by UdpChannel.isEquivalentTo when two channel URIs sharing the same matching tag declare different control-mode values. Tagged channels that are expected to alias the same transport must agree on control-mode.

Solutions

  1. Make control-mode identical for all URIs sharing the same tag
  2. Use distinct tags for channels with different control modes
  3. Remove the matching tag if the channels are intentionally different

Example fix

// before
"aeron:udp?tags=1001|control-mode=manual|control=..." and "aeron:udp?tags=1001|control-mode=common"
// after
"aeron:udp?tags=1001|control-mode=manual|control=..." and "aeron:udp?tags=1002|control-mode=common"
Defensive patterns

Strategy: validation

Validate before calling

Map<String,String> tagToMode = new HashMap<>();
String mode = extractParam(uri, "control-mode");
String prev = tagToMode.putIfAbsent(tag, mode);
if (prev != null && !prev.equals(mode)) throw new IllegalArgumentException("tag " + tag + " reused with different control-mode");

Type guard

static boolean controlModesMatch(String uriA, String uriB) { return Objects.equals(extractParam(uriA, "control-mode"), extractParam(uriB, "control-mode")); }

Try / catch

try { return channel.isEquivalentTo(other); } catch (IllegalArgumentException e) { log.warn("tagged channel mismatch: {}", e.getMessage()); return false; }

Prevention

When it happens

Trigger: Creating two subscriptions/publications whose URIs both set tags=... (matching tag) but one uses control-mode=manual and the other control-mode=common (or omits it).

Common situations: MDC setups where a dynamic control channel and a manual one reuse the same tag, config drift between services sharing a tag.

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

Appendix: source

Thrown at aeron-driver/src/main/java/io/aeron/driver/media/UdpChannel.java:793

     *
     * @param udpChannel to match against.
     * @param localAddress local address override to use for this channel.
     * @param remoteAddress remote address override to use for this channel.
     * @return true if there is a match otherwise false.
     */
    public boolean matchesTag(
        final UdpChannel udpChannel,
        final InetSocketAddress localAddress,
        final InetSocketAddress remoteAddress)
    {
        if (!hasTag || !udpChannel.hasTag() || tag != udpChannel.tag())
        {
            return false;
        }

        if (!hasMatchingControlMode(udpChannel))
        {
            throw new IllegalArgumentException(
                "matching tag=" + tag + " has mismatched control-mode: " + uriStr + " <> " + udpChannel.uriStr);
        }

        if (!hasMatchingAddress(udpChannel, localAddress, remoteAddress))
        {
            throw new IllegalArgumentException(
                "matching tag=" + tag + " has mismatched endpoint or control: " + uriStr + " <> " + udpChannel.uriStr);
        }

        return true;
    }

    private boolean isWildcard()
    {
        return remoteData.getAddress().isAnyLocalAddress() &&
            remoteData.getPort() == 0 &&
            localData.getAddress().isAnyLocalAddress() &&
            localData.getPort() == 0;

View on GitHub (pinned to 6d60124e15)