{"record":{"id":"22064815c8a7d5f3","repo":"grpc/grpc-java","slug":"stringmatcher-expected-a-string-input-but-receive","errorCode":null,"errorMessage":"StringMatcher expected a String input, but received: ${value class name}","messagePattern":"StringMatcher expected a String input, but received: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java","lineNumber":100,"sourceCode":"      return matcher.match(input.apply(context));\n    }\n    \n\n\n    private static final class StringMatcherAdapter implements Matcher {\n      private final Matchers.StringMatcher stringMatcher;\n\n      StringMatcherAdapter(Matchers.StringMatcher stringMatcher) {\n        this.stringMatcher = stringMatcher;\n      }\n\n      @Override\n      public boolean match(Object value) {\n        if (value == null) {\n          return false;\n        }\n        if (!(value instanceof String)) {\n          throw new IllegalArgumentException(\n              \"StringMatcher expected a String input, but received: \" \n              + value.getClass().getName());\n        }\n        return stringMatcher.matches((String) value);\n      }\n\n      @Override\n      public Class<?> inputType() {\n        return String.class;\n      }\n    }\n  }\n  \n  private static final class OrMatcherEvaluator extends PredicateEvaluator {\n    private final List<PredicateEvaluator> evaluators;\n    \n    OrMatcherEvaluator(Predicate.PredicateList proto) {\n      if (proto.getPredicateCount() < 2) {","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/xds/src/main/java/io/grpc/xds/internal/matcher/PredicateEvaluator.java#L82-L118","documentation":"The adapter exposing StringMatcher as a generic Matcher only accepts String values. If evaluate() hands it a non-String (non-null) value, it throws this IllegalArgumentException naming the actual received class, since StringMatcher.matches only understands strings.","triggerScenarios":"Running a matcher tree where a SinglePredicate's input produces a non-String value (e.g. an integer or boolean output from a custom MatchInput) but the matcher is a StringMatcherAdapter wrapping a StringMatcher; type checks at construction passed or were bypassed.","commonSituations":"Custom MatchInput implementations whose outputType() claim doesn't match the produced runtime type; config where value_match is combined with a binary/numeric input; reflection or dynamic input resolution returning unexpected types.","solutions":["Fix the MatchInput so it outputs a String (or its outputType() declaration matches the matcher's expected String input)","Replace value_match with a custom_match whose matcher accepts the input's actual type","Wrap custom inputs so they stringify their output before reaching StringMatcher"],"exampleFix":"// before\npublic Object evaluate(MatchContext ctx) {\n  return ctx.request().headerCount(); // Integer into StringMatcher\n}\n// after\npublic Object evaluate(MatchContext ctx) {\n  return String.valueOf(ctx.request().headerCount());\n}","handlingStrategy":"type-guard","validationCode":"if (matcher.inputType() != String.class && !(value instanceof String)) {\n  throw new IllegalStateException(\"StringMatcher fed non-String value\");\n}","typeGuard":"static boolean isStringValue(Object v) { return v instanceof String; }","tryCatchPattern":"try { boolean r = evaluator.evaluate(ctx); } catch (IllegalArgumentException e) { log.error(\"Matcher input type error: \" + e.getMessage()); return MatchResult.noMatch(); }","preventionTips":["Make MatchInput.outputType() reflect the real runtime type produced","Unit-test custom MatchInputs against their declared output types","Avoid dynamic inputs that can return varying types"],"tags":["xds","matcher","type-mismatch","runtime"],"backgroundTag":"type-mismatch","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"}