{"record":{"id":"29112344ad4b9f50","repo":"grpc/grpc-java","slug":"unsupported-custom-match-matcher-typeurl","errorCode":null,"errorMessage":"Unsupported custom_match matcher: ${typeUrl}","messagePattern":"Unsupported custom_match matcher: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java","lineNumber":65,"sourceCode":"    private final MatchInput input;\n    private final Matcher matcher;\n    \n    SinglePredicateEvaluator(Predicate.SinglePredicate proto) {\n      if (!proto.hasInput()) {\n        throw new IllegalArgumentException(\"SinglePredicate must have input\");\n      }\n      this.input = UnifiedMatcher.resolveInput(proto.getInput());\n      \n      if (proto.hasValueMatch()) {\n        Matchers.StringMatcher stringMatcher =\n            MatcherParser.parseStringMatcher(proto.getValueMatch());\n        this.matcher = new StringMatcherAdapter(stringMatcher);\n      } else if (proto.hasCustomMatch()) {\n        TypedExtensionConfig customConfig = proto.getCustomMatch();\n        MatcherProvider provider = MatcherRegistry.getDefaultRegistry()\n            .getMatcherProvider(customConfig.getTypedConfig().getTypeUrl());\n        if (provider == null) {\n          throw new IllegalArgumentException(\"Unsupported custom_match matcher: \" \n              + customConfig.getTypedConfig().getTypeUrl());\n        }\n        this.matcher = provider.getMatcher(customConfig);\n      } else {\n        throw new IllegalArgumentException(\n            \"SinglePredicate must have either value_match or custom_match\");\n      }\n\n      if (!input.outputType().equals(matcher.inputType())) {\n        throw new IllegalArgumentException(\"Type mismatch: input \" + input.outputType().getName()\n            + \" not compatible with matcher \" + matcher.inputType().getName());\n      }\n    }\n    \n    @Override \n    boolean evaluate(MatchContext context) {\n      return matcher.match(input.apply(context));\n    }","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Register a MatcherProvider for the typeUrl via MatcherRegistry.getDefaultRegistry().register(...) before parsing","Remove or replace the custom_match with a supported value_match","Check gRPC/xDS version compatibility with the control plane and upgrade the client if it should support the extension","Verify the '@type' URL string exactly matches the registered type (correct package/name, no typos)"],"exampleFix":"// before: parsing with unregistered provider\nMatcherRegistry.getDefaultRegistry().register(\"type.googleapis.com/my.CustomMatcher\",\n    config -> new MyCustomMatcher(config));\nPredicateEvaluator.fromProto(predicate); // now works\n// after\nMatcherProvider provider = MatcherRegistry.getDefaultRegistry()\n    .getMatcherProvider(typeUrl);\nif (provider == null) { throw new IllegalArgumentException(\n    \"Unsupported custom_match matcher: \" + typeUrl); }","handlingStrategy":"try-catch","validationCode":"String typeUrl = customMatch.getTypedConfig().getTypeUrl();\nboolean supported = MatcherRegistry.getDefaultRegistry().getMatcherProvider(typeUrl) != null;","typeGuard":null,"tryCatchPattern":"try { new PredicateEvaluator.SinglePredicateEvaluator(proto); } catch (IllegalArgumentException e) { fallbackToDefaultMatcher(proto); }","preventionTips":["Register all custom MatcherProviders at application startup","Keep client gRPC/xDS versions in sync with the control plane","Copy '@type' URLs exactly as documented, no ad-hoc edits"],"tags":["xds","extension","custom-matcher","grpc"],"backgroundTag":"unsupported-operation","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"}