grpc/grpc-java · error · IllegalArgumentException

MatcherTree prefix_match_map must contain at least one entry

Error message

MatcherTree prefix_match_map must contain at least one entry

What it means

If a MatcherTree specifies prefix_match_map, the map must contain at least one entry; an empty prefix map gives the tree nothing to match and is rejected at construction. This enforces the non-empty map invariant before building the prefix trie.

Solutions

  1. Populate prefix_match_map with at least one prefix -> on_match entry.
  2. Validate the map is non-empty before sending the config from the management server.
  3. If prefix matching is not needed, remove prefix_match_map and use exact_match_map.

Example fix

// before
{"matcher_tree": {"input": {...}, "prefix_match_map": {"map": {}}}}
// after
{"matcher_tree": {"input": {...}, "prefix_match_map": {"map": {"/api/": {"matcher": {...}}}}}}
Defensive patterns

Strategy: validation

Validate before calling

if (treeProto.hasPrefixMatchMap() && treeProto.getPrefixMatchMap().getMapCount() == 0) {
  throw new IllegalArgumentException("prefix_match_map present but empty");
}

Type guard

boolean hasNonEmptyPrefixMap(Matcher.MatcherTree proto) {
  return !proto.hasPrefixMatchMap() || proto.getPrefixMatchMap().getMapCount() > 0;
}

Try / catch

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

Prevention

When it happens

Trigger: Constructing MatcherTree from a proto with hasPrefixMatchMap() true but matchMap.getMapCount() == 0 (map present but empty).

Common situations: Control plane emitting prefix routing config with all entries filtered out, or templated matcher config where prefix entries were not substituted.

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/9809ad3036598cc6. Report an issue: GitHub.

Appendix: source

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

    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()) {
        this.prefixTrie.insert(entry.getKey(),
            new OnMatch(entry.getValue(), actionValidator));
      }
      this.exactMatchMap = null;
    } else {
      throw new IllegalArgumentException(
          "MatcherTree must have either exact_match_map or prefix_match_map");
    }
    if (onNoMatchProto != null) {
      this.onNoMatch = new OnMatch(onNoMatchProto, actionValidator);
    } else {
      this.onNoMatch = null;
    }

View on GitHub (pinned to 64daddc1f3)