grpc/grpc-java · error · IllegalArgumentException

MatcherTree exact_match_map must contain at least one entry

Error message

MatcherTree exact_match_map must contain at least one entry

What it means

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.

Solutions

  1. Populate exact_match_map with at least one key -> on_match entry.
  2. Validate the map is non-empty on the management server before emitting the config.
  3. If the map can be empty, remove the exact_match_map field and provide a prefix_match_map or restructure the matcher.

Example fix

// before
{"matcher_tree": {"input": {...}, "exact_match_map": {"map": {}}}}
// after
{"matcher_tree": {"input": {...}, "exact_match_map": {"map": {"route-a": {"matcher": {...}}}}}}
Defensive patterns

Strategy: validation

Validate before calling

if (treeProto.hasExactMatchMap() && treeProto.getExactMatchMap().getMapCount() == 0) {
  throw new IllegalArgumentException("exact_match_map present but empty");
}

Type guard

boolean hasNonEmptyExactMap(Matcher.MatcherTree proto) {
  return !proto.hasExactMatchMap() || proto.getExactMatchMap().getMapCount() > 0;
}

Try / catch

try {
  matcherTree = new MatcherTree(proto, onNoMatch, validator);
} catch (IllegalArgumentException e) {
  logger.warn("empty exact_match_map in config: " + e.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: Constructing MatcherTree from a proto with hasExactMatchMap() true but matchMap.getMapCount() == 0 (the map field set but empty).

Common situations: Templated config rendering with an empty map, or control-plane code setting exact_match_map without adding entries after filtering.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/f77f1799f0cfff16. Report an issue: GitHub.

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/internal/matcher/MatcherTree.java:60

    if (!proto.hasInput()) {
      throw new IllegalArgumentException("MatcherTree must have input");
    }
    if (proto.getInput().getTypedConfig().getTypeUrl()
        .equals(TYPE_URL_HTTP_ATTRIBUTES_CEL_INPUT)) {
      throw new IllegalArgumentException(
          "HttpAttributesCelMatchInput cannot be used with MatcherTree");
    }
    
    if (proto.hasCustomMatch()) {
      throw new IllegalArgumentException("MatcherTree does not support custom_match");
    }
    
    this.input = UnifiedMatcher.resolveInput(proto.getInput());
    
    if (proto.hasExactMatchMap()) {
      Matcher.MatcherTree.MatchMap matchMap = proto.getExactMatchMap();
      if (matchMap.getMapCount() == 0) {
        throw new IllegalArgumentException(
            "MatcherTree exact_match_map must contain at least one entry");
      }
      this.exactMatchMap = new HashMap<>();
      for (Map.Entry<String, Matcher.OnMatch> entry : 
          matchMap.getMapMap().entrySet()) {
        this.exactMatchMap.put(entry.getKey(),
            new OnMatch(entry.getValue(), actionValidator));
      }
      this.prefixTrie = null;
    } else if (proto.hasPrefixMatchMap()) {
      Matcher.MatcherTree.MatchMap matchMap = proto.getPrefixMatchMap();
      if (matchMap.getMapCount() == 0) {
        throw new IllegalArgumentException(
            "MatcherTree prefix_match_map must contain at least one entry");
      }
      this.prefixTrie = new PrefixTrie();
      for (Map.Entry<String, Matcher.OnMatch> entry : 
          matchMap.getMapMap().entrySet()) {

View on GitHub (pinned to 64daddc1f3)