aeron-io/aeron · error · IllegalStateException

empty key not allowed at index

Error message

empty key not allowed at index <i> in <uri>

What it means

ChannelUri.parse() tokenizes an Aeron channel URI string with a small state machine. While parsing the query-parameter section, when it encounters '=' it requires that a non-empty key has accumulated; if nothing was accumulated (e.g. the URI starts a param with '=') it throws this IllegalStateException to reject a malformed URI.

Solutions

  1. Inspect the URI string at the reported index <i> and fix the malformed parameter so every '=' has a non-empty key before it
  2. Build URIs with ChannelUriStringBuilder (with validate()) instead of string concatenation
  3. Wrap parse() in try-catch for IllegalStateException if URIs come from external input, and surface a clear config error

Example fix

// before
ChannelUri uri = ChannelUri.parse("aeron:udp?endpoint=localhost:40456|=1");
// after
ChannelUri uri = ChannelUri.parse("aeron:udp?endpoint=localhost:40456|ttl=1");
Defensive patterns

Strategy: validation

Validate before calling

static void validateNoEmptyKeys(String uri) {
    int q = uri.indexOf('?');
    if (q < 0) return;
    for (String p : uri.substring(q + 1).split("\\|")) {
        int eq = p.indexOf('=');
        if (eq == 0) throw new IllegalArgumentException("empty key in: " + uri);
    }
}

Type guard

static boolean hasWellFormedParams(String uri) {
    int q = uri.indexOf('?');
    if (q < 0) return true;
    for (String p : uri.substring(q + 1).split("\\|")) {
        if (p.indexOf('=') <= 0) return false;
    }
    return true;
}

Try / catch

try {
    ChannelUri uri = ChannelUri.parse(channelUri);
} catch (IllegalStateException e) {
    throw new IllegalArgumentException("Malformed Aeron channel URI: " + channelUri, e);
}

Prevention

When it happens

Trigger: Calling ChannelUri.parse() on a URI whose params section contains an empty key, e.g. "aeron:udp?endpoint=localhost:40456|=value|alias=x" or "aeron:udp?|k=v" — a '=' is reached with an empty key builder.

Common situations: Programmatically building channel URIs with string concatenation where a param name is empty or an extra '|' or stray '=' sneaks in; hand-edited configuration where a parameter name was deleted but the '=' remained.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at aeron-client/src/main/java/io/aeron/ChannelUri.java:439

                            break;

                        case ':':
                        case '|':
                        case '=':
                            throw new IllegalArgumentException(
                                "encountered '" + c + "' within media definition at index " + i + " in " + uri);

                        default:
                            builder.append(c);
                    }
                    break;

                case PARAMS_KEY:
                    if (c == '=')
                    {
                        if (builder.isEmpty())
                        {
                            throw new IllegalStateException("empty key not allowed at index " + i + " in " + uri);
                        }
                        key = builder.toString();
                        builder.setLength(0);
                        state = State.PARAMS_VALUE;
                    }
                    else
                    {
                        if (c == '|')
                        {
                            throw new IllegalStateException("invalid end of key at index " + i + " in " + uri);
                        }
                        builder.append(c);
                    }
                    break;

                case PARAMS_VALUE:
                    if (c == '|')
                    {

View on GitHub (pinned to 6d60124e15)