grpc/grpc-java · error · IllegalArgumentException

HttpAttributesCelMatchInput cannot be used with MatcherTree

Error message

HttpAttributesCelMatchInput cannot be used with MatcherTree

What it means

MatcherTree matches on a single scalar key derived from its input; HttpAttributesCelMatchInput (a CEL-based multi-attribute input) is incompatible with tree matching, so the constructor rejects it explicitly. Use it with MatcherList predicates instead, where multiple CEL expressions can be evaluated.

Solutions

  1. Replace the input with a single-key input like HttpRequestHeaderMatchInput.
  2. Move the CEL expression into a MatcherList FieldMatcher predicate (custom_match with CelMatchInput) instead of a MatcherTree.
  3. If tree semantics are required, compute the key upstream (e.g. into a header) and match on that header.

Example fix

// before
{"matcher_tree": {"input": {"typed_config": {"@type": ".../HttpAttributesCelMatchInput"}}, "exact_match_map": {...}}}
// after
{"matcher_tree": {"input": {"typed_config": {"@type": ".../HttpRequestHeaderMatchInput", "header_name": "x-key"}}, "exact_match_map": {...}}}
Defensive patterns

Strategy: validation

Validate before calling

String typeUrl = treeProto.getInput().getTypedConfig().getTypeUrl();
if (typeUrl.endsWith("HttpAttributesCelMatchInput")) {
  throw new IllegalArgumentException("HttpAttributesCelMatchInput is not valid for MatcherTree");
}

Type guard

boolean isTreeCompatibleInput(TypedExtensionConfig input) {
  return !input.getTypedConfig().getTypeUrl().endsWith("HttpAttributesCelMatchInput");
}

Try / catch

try {
  matcherTree = new MatcherTree(proto, onNoMatch, validator);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("HttpAttributesCelMatchInput")) {
    // rewrite as MatcherList with CEL predicate
  }
  throw e;
}

Prevention

When it happens

Trigger: Constructing MatcherTree whose input TypedExtensionConfig type_url equals TYPE_URL_HTTP_ATTRIBUTES_CEL_INPUT (HttpAttributesCelMatchInput).

Common situations: Reusing the same input config block when switching a matcher from list form to tree form; copying an Envoy config that uses CEL input with a list matcher into a tree matcher.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

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

final class MatcherTree extends UnifiedMatcher {
  private static final String TYPE_URL_HTTP_ATTRIBUTES_CEL_INPUT =
      "type.googleapis.com/xds.type.matcher.v3.HttpAttributesCelMatchInput";
  private final MatchInput input;
  @Nullable 
  private final Map<String, OnMatch> exactMatchMap;
  @Nullable 
  private final PrefixTrie prefixTrie;
  @Nullable 
  private final OnMatch onNoMatch;
  
  MatcherTree(Matcher.MatcherTree proto, @Nullable Matcher.OnMatch onNoMatchProto,
      Predicate<String> actionValidator) {
    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()) {

View on GitHub (pinned to 64daddc1f3)