grpc/grpc-java · error · IllegalArgumentException

keys in KeyBuilder must be unique

Error message

keys in KeyBuilder must be unique

What it means

The RLS KeyBuilder collects request-header keys: constant keys, extra keys, and per-NameMatcher keys. Because all of these become distinct keys in the RLS request, duplicates are forbidden; checkUniqueKey throws this IllegalArgumentException when the combined set is smaller than the sum of the individual counts, proving at least one duplicate key.

Solutions

  1. Review all keys passed to constantKeys, EXTRA_KEY_NAMES, and every NameMatcher; rename or remove duplicates so each key appears exactly once.
  2. If a key must have both a constant and matcher behavior, keep only one and restructure the matching config.
  3. Pre-validate uniqueness in code: collect keys into a Set and assert size before building the RLS policy.
  4. Document reserved extra keys for the team so no custom key collides with them.

Example fix

// before
KeyBuilder.create().addConstantKey("env", "prod").addNameMatcher(NameMatcher.create("env"));
// after
KeyBuilder.create().addConstantKey("env", "prod"); // matcher removed; key used once
Defensive patterns

Strategy: validation

Validate before calling

static void assertKeysUnique(Set<String> constants, Set<String> extras, List<NameMatcher> matchers) {
  Set<String> all = new HashSet<>();
  all.addAll(constants);
  all.addAll(extras);
  matchers.forEach(m -> all.add(m.key()));
  if (all.size() != constants.size() + extras.size() + matchers.size()) {
    throw new IllegalArgumentException("duplicate RLS keys");
  }
}

Prevention

When it happens

Trigger: Building an RLS KeyBuilder where the same key string appears in more than one place: a constant key equal to a NameMatcher key, two NameMatchers sharing a key, or a key colliding with the reserved extra keys ('host', 'path', 'service', 'method' style EXTRA_KEY_NAMES).

Common situations: Configuring RLS header matching where a developer adds both a constant value and a header matcher for the same header, or unintentionally reuses a key that the extra-keys set reserves.

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 grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/b20a451483abc2ff. Report an issue: GitHub.

Appendix: source

Thrown at rls/src/main/java/io/grpc/rls/RlsProtoConverters.java:274

          (Map<String, String>) JsonUtil.getObject(keyBuilder,  "constantKeys");
      if (constantKeys == null) {
        constantKeys = ImmutableMap.of();
      }
      ImmutableList<NameMatcher> nameMatchers = nameMatchersBuilder.build();
      checkUniqueKey(nameMatchers, constantKeys.keySet());
      return GrpcKeyBuilder.create(
          namesBuilder.build(), nameMatchers, extraKeys, ImmutableMap.copyOf(constantKeys));
    }
  }

  private static void checkUniqueKey(List<NameMatcher> nameMatchers, Set<String> constantKeys) {
    Set<String> keys = new HashSet<>(constantKeys);
    keys.addAll(EXTRA_KEY_NAMES);
    for (NameMatcher nameMatcher :  nameMatchers) {
      keys.add(nameMatcher.key());
    }
    if (keys.size() != nameMatchers.size() + constantKeys.size() + EXTRA_KEY_NAMES.size()) {
      throw new IllegalArgumentException("keys in KeyBuilder must be unique");
    }
  }

  private RlsProtoConverters() {}
}

View on GitHub (pinned to 64daddc1f3)