{"record":{"id":"b20a451483abc2ff","repo":"grpc/grpc-java","slug":"keys-in-keybuilder-must-be-unique","errorCode":null,"errorMessage":"keys in KeyBuilder must be unique","messagePattern":"keys in KeyBuilder must be unique","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"rls/src/main/java/io/grpc/rls/RlsProtoConverters.java","lineNumber":274,"sourceCode":"          (Map<String, String>) JsonUtil.getObject(keyBuilder,  \"constantKeys\");\n      if (constantKeys == null) {\n        constantKeys = ImmutableMap.of();\n      }\n      ImmutableList<NameMatcher> nameMatchers = nameMatchersBuilder.build();\n      checkUniqueKey(nameMatchers, constantKeys.keySet());\n      return GrpcKeyBuilder.create(\n          namesBuilder.build(), nameMatchers, extraKeys, ImmutableMap.copyOf(constantKeys));\n    }\n  }\n\n  private static void checkUniqueKey(List<NameMatcher> nameMatchers, Set<String> constantKeys) {\n    Set<String> keys = new HashSet<>(constantKeys);\n    keys.addAll(EXTRA_KEY_NAMES);\n    for (NameMatcher nameMatcher :  nameMatchers) {\n      keys.add(nameMatcher.key());\n    }\n    if (keys.size() != nameMatchers.size() + constantKeys.size() + EXTRA_KEY_NAMES.size()) {\n      throw new IllegalArgumentException(\"keys in KeyBuilder must be unique\");\n    }\n  }\n\n  private RlsProtoConverters() {}\n}\n","sourceCodeStart":256,"sourceCodeEnd":280,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/rls/src/main/java/io/grpc/rls/RlsProtoConverters.java#L256-L280","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Review all keys passed to constantKeys, EXTRA_KEY_NAMES, and every NameMatcher; rename or remove duplicates so each key appears exactly once.","If a key must have both a constant and matcher behavior, keep only one and restructure the matching config.","Pre-validate uniqueness in code: collect keys into a Set and assert size before building the RLS policy.","Document reserved extra keys for the team so no custom key collides with them."],"exampleFix":"// before\nKeyBuilder.create().addConstantKey(\"env\", \"prod\").addNameMatcher(NameMatcher.create(\"env\"));\n// after\nKeyBuilder.create().addConstantKey(\"env\", \"prod\"); // matcher removed; key used once","handlingStrategy":"validation","validationCode":"static void assertKeysUnique(Set<String> constants, Set<String> extras, List<NameMatcher> matchers) {\n  Set<String> all = new HashSet<>();\n  all.addAll(constants);\n  all.addAll(extras);\n  matchers.forEach(m -> all.add(m.key()));\n  if (all.size() != constants.size() + extras.size() + matchers.size()) {\n    throw new IllegalArgumentException(\"duplicate RLS keys\");\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep a single source of truth for RLS keys and derive all three key groups from it.","Document reserved extra-key names so custom keys cannot collide.","Unit-test KeyBuilder construction with the production config.","Fail at config-load time, not at request time, by building the policy eagerly."],"tags":["grpc","rls","config","duplicate-keys"],"backgroundTag":"conflicting-config-options","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}