grpc/grpc-java · error · IllegalArgumentException

Unsupported custom_match matcher

Error message

Unsupported custom_match matcher: ${typeUrl}

What it means

When a SinglePredicate uses custom_match, the typed config's type URL must be registered in the MatcherRegistry as a MatcherProvider. If no provider is registered for that type URL, the evaluator throws this IllegalArgumentException — the custom matcher extension is unknown to this runtime.

Solutions

  1. Register a MatcherProvider for the typeUrl via MatcherRegistry.getDefaultRegistry().register(...) before parsing
  2. Remove or replace the custom_match with a supported value_match
  3. Check gRPC/xDS version compatibility with the control plane and upgrade the client if it should support the extension
  4. Verify the '@type' URL string exactly matches the registered type (correct package/name, no typos)

Example fix

// before: parsing with unregistered provider
MatcherRegistry.getDefaultRegistry().register("type.googleapis.com/my.CustomMatcher",
    config -> new MyCustomMatcher(config));
PredicateEvaluator.fromProto(predicate); // now works
// after
MatcherProvider provider = MatcherRegistry.getDefaultRegistry()
    .getMatcherProvider(typeUrl);
if (provider == null) { throw new IllegalArgumentException(
    "Unsupported custom_match matcher: " + typeUrl); }
Defensive patterns

Strategy: try-catch

Validate before calling

String typeUrl = customMatch.getTypedConfig().getTypeUrl();
boolean supported = MatcherRegistry.getDefaultRegistry().getMatcherProvider(typeUrl) != null;

Try / catch

try { new PredicateEvaluator.SinglePredicateEvaluator(proto); } catch (IllegalArgumentException e) { fallbackToDefaultMatcher(proto); }

Prevention

When it happens

Trigger: Calling SinglePredicateEvaluator with a Predicate whose custom_match TypedExtensionConfig has a typeUrl not present in MatcherRegistry.getDefaultRegistry() — e.g. '@type' points to a matcher extension type this gRPC version does not implement.

Common situations: Envoy-originated configs referencing envoy custom matcher extensions not ported to gRPC xDS; version skew where the control plane is newer than the client; typo in the type.googleapis.com URL; custom matcher plugin not registered via MatcherRegistry before parsing.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java:65

    private final MatchInput input;
    private final Matcher matcher;
    
    SinglePredicateEvaluator(Predicate.SinglePredicate proto) {
      if (!proto.hasInput()) {
        throw new IllegalArgumentException("SinglePredicate must have input");
      }
      this.input = UnifiedMatcher.resolveInput(proto.getInput());
      
      if (proto.hasValueMatch()) {
        Matchers.StringMatcher stringMatcher =
            MatcherParser.parseStringMatcher(proto.getValueMatch());
        this.matcher = new StringMatcherAdapter(stringMatcher);
      } else if (proto.hasCustomMatch()) {
        TypedExtensionConfig customConfig = proto.getCustomMatch();
        MatcherProvider provider = MatcherRegistry.getDefaultRegistry()
            .getMatcherProvider(customConfig.getTypedConfig().getTypeUrl());
        if (provider == null) {
          throw new IllegalArgumentException("Unsupported custom_match matcher: " 
              + customConfig.getTypedConfig().getTypeUrl());
        }
        this.matcher = provider.getMatcher(customConfig);
      } else {
        throw new IllegalArgumentException(
            "SinglePredicate must have either value_match or custom_match");
      }

      if (!input.outputType().equals(matcher.inputType())) {
        throw new IllegalArgumentException("Type mismatch: input " + input.outputType().getName()
            + " not compatible with matcher " + matcher.inputType().getName());
      }
    }
    
    @Override 
    boolean evaluate(MatchContext context) {
      return matcher.match(input.apply(context));
    }

View on GitHub (pinned to 64daddc1f3)