{"record":{"id":"f77f1799f0cfff16","repo":"grpc/grpc-java","slug":"matchertree-exact-match-map-must-contain-at-least","errorCode":null,"errorMessage":"MatcherTree exact_match_map must contain at least one entry","messagePattern":"MatcherTree exact_match_map must contain at least one entry","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/MatcherTree.java","lineNumber":60,"sourceCode":"    if (!proto.hasInput()) {\n      throw new IllegalArgumentException(\"MatcherTree must have input\");\n    }\n    if (proto.getInput().getTypedConfig().getTypeUrl()\n        .equals(TYPE_URL_HTTP_ATTRIBUTES_CEL_INPUT)) {\n      throw new IllegalArgumentException(\n          \"HttpAttributesCelMatchInput cannot be used with MatcherTree\");\n    }\n    \n    if (proto.hasCustomMatch()) {\n      throw new IllegalArgumentException(\"MatcherTree does not support custom_match\");\n    }\n    \n    this.input = UnifiedMatcher.resolveInput(proto.getInput());\n    \n    if (proto.hasExactMatchMap()) {\n      Matcher.MatcherTree.MatchMap matchMap = proto.getExactMatchMap();\n      if (matchMap.getMapCount() == 0) {\n        throw new IllegalArgumentException(\n            \"MatcherTree exact_match_map must contain at least one entry\");\n      }\n      this.exactMatchMap = new HashMap<>();\n      for (Map.Entry<String, Matcher.OnMatch> entry : \n          matchMap.getMapMap().entrySet()) {\n        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()) {","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/MatcherTree.java#L42-L78","documentation":"If a MatcherTree specifies exact_match_map, the map must contain at least one entry; an empty exact-match map is a config error because the tree would have no keys to match against. The constructor validates the count eagerly.","triggerScenarios":"Constructing MatcherTree from a proto with hasExactMatchMap() true but matchMap.getMapCount() == 0 (the map field set but empty).","commonSituations":"Templated config rendering with an empty map, or control-plane code setting exact_match_map without adding entries after filtering.","solutions":["Populate exact_match_map with at least one key -> on_match entry.","Validate the map is non-empty on the management server before emitting the config.","If the map can be empty, remove the exact_match_map field and provide a prefix_match_map or restructure the matcher."],"exampleFix":"// before\n{\"matcher_tree\": {\"input\": {...}, \"exact_match_map\": {\"map\": {}}}}\n// after\n{\"matcher_tree\": {\"input\": {...}, \"exact_match_map\": {\"map\": {\"route-a\": {\"matcher\": {...}}}}}}","handlingStrategy":"validation","validationCode":"if (treeProto.hasExactMatchMap() && treeProto.getExactMatchMap().getMapCount() == 0) {\n  throw new IllegalArgumentException(\"exact_match_map present but empty\");\n}","typeGuard":"boolean hasNonEmptyExactMap(Matcher.MatcherTree proto) {\n  return !proto.hasExactMatchMap() || proto.getExactMatchMap().getMapCount() > 0;\n}","tryCatchPattern":"try {\n  matcherTree = new MatcherTree(proto, onNoMatch, validator);\n} catch (IllegalArgumentException e) {\n  logger.warn(\"empty exact_match_map in config: \" + e.getMessage());\n  throw e;\n}","preventionTips":["Never emit an empty exact_match_map; omit the field instead","Assert map non-empty after template rendering","Validate configs before publish on the management server"],"tags":["xds","matcher","config-validation"],"backgroundTag":"empty-required-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"}