{"record":{"id":"bfc5d4671c927b66","repo":"grpc/grpc-java","slug":"matchertree-must-have-either-exact-match-map-or-pr","errorCode":null,"errorMessage":"MatcherTree must have either exact_match_map or prefix_match_map","messagePattern":"MatcherTree must have either exact_match_map or prefix_match_map","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/MatcherTree.java","lineNumber":84,"sourceCode":"        this.exactMatchMap.put(entry.getKey(),\n            new OnMatch(entry.getValue(), actionValidator));\n      }\n      this.prefixTrie = null;\n    } else if (proto.hasPrefixMatchMap()) {\n      Matcher.MatcherTree.MatchMap matchMap = proto.getPrefixMatchMap();\n      if (matchMap.getMapCount() == 0) {\n        throw new IllegalArgumentException(\n            \"MatcherTree prefix_match_map must contain at least one entry\");\n      }\n      this.prefixTrie = new PrefixTrie();\n      for (Map.Entry<String, Matcher.OnMatch> entry : \n          matchMap.getMapMap().entrySet()) {\n        this.prefixTrie.insert(entry.getKey(),\n            new OnMatch(entry.getValue(), actionValidator));\n      }\n      this.exactMatchMap = null;\n    } else {\n      throw new IllegalArgumentException(\n          \"MatcherTree must have either exact_match_map or prefix_match_map\");\n    }\n    if (onNoMatchProto != null) {\n      this.onNoMatch = new OnMatch(onNoMatchProto, actionValidator);\n    } else {\n      this.onNoMatch = null;\n    }\n  }\n\n  @Override\n  MatchResult match(MatchContext context) {\n\n    Object valueObj = input.apply(context);\n    if (!(valueObj instanceof String)) {\n      return onNoMatch != null ? onNoMatch.evaluate(context) : MatchResult.noMatch();\n    }\n    String value = (String) valueObj;\n    if (exactMatchMap != null) {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/MatcherTree.java#L66-L102","documentation":"A MatcherTree must specify exactly one of exact_match_map or prefix_match_map; if neither is set, the constructor throws because the tree has no matching strategy. This is the final else branch after checking both oneof fields.","triggerScenarios":"Constructing MatcherTree from a Matcher.MatcherTree proto that has an input (and no custom_match) but neither exact_match_map nor prefix_match_map set.","commonSituations":"Configs authored against a newer Envoy API that supports other tree arms (e.g. list_match) being loaded by this implementation, or truncated matcher config from the control plane.","solutions":["Add either exact_match_map or prefix_match_map with at least one entry.","Ensure the config was not truncated and the intended map field is inside matcher_tree.","Check version compatibility: fields supported by the control plane must also be supported here."],"exampleFix":"// before\n{\"matcher_tree\": {\"input\": {...}}}\n// after\n{\"matcher_tree\": {\"input\": {...}, \"exact_match_map\": {\"map\": {\"key\": {\"matcher\": {...}}}}}}","handlingStrategy":"validation","validationCode":"boolean hasMap = treeProto.hasExactMatchMap() || treeProto.hasPrefixMatchMap();\nif (!hasMap) {\n  throw new IllegalArgumentException(\"MatcherTree needs exact_match_map or prefix_match_map\");\n}","typeGuard":"boolean hasTreeMatchStrategy(Matcher.MatcherTree proto) {\n  return proto.hasExactMatchMap() || proto.hasPrefixMatchMap();\n}","tryCatchPattern":"try {\n  matcherTree = new MatcherTree(proto, onNoMatch, validator);\n} catch (IllegalArgumentException e) {\n  logger.warn(\"MatcherTree has no match map: \" + e.getMessage());\n  throw new StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription(e.getMessage()));\n}","preventionTips":["Ensure templates always render one of the two map fields","Check control-plane fields are supported by this gRPC version before using them","Add round-trip validation after config generation"],"tags":["xds","matcher","missing-required-field"],"backgroundTag":"missing-required-config-field","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"}