grpc/grpc-java · error · IllegalArgumentException

Unknown StringMatcher match pattern:

Error message

Unknown StringMatcher match pattern: 

What it means

parseStringMatcher(envoy StringMatcher) switches over MatchPatternCase; when the Envoy StringMatcher has MATCHPATTERN_NOT_SET (or an unknown case) there is no valid match pattern, so IllegalArgumentException is thrown. The xDS config's string matcher (used inside header/path matchers) is empty or uses an unsupported pattern type.

Solutions

  1. Set exactly one match pattern field (exact, safe_regex, prefix, suffix, contains) on the StringMatcher in the xDS config
  2. Upgrade grpc-xds so the MatchPatternCase is recognized
  3. Inspect the logged MatchPatternCase value to find the offending config entry
  4. Regenerate config from a template that always populates the pattern

Example fix

// before
{"string_match":{}}
// after
{"string_match":{"exact":"v2"}}
Defensive patterns

Strategy: validation

Validate before calling

if (proto.getMatchPatternCase() == StringMatcher.MatchPatternCase.MATCHPATTERN_NOT_SET) { reject("string_match empty"); }

Type guard

null

Try / catch

try { sm = MatcherParser.parseStringMatcher(proto); }
catch (IllegalArgumentException e) { log.error("Bad StringMatcher: " + e.getMessage()); reject(); }

Prevention

When it happens

Trigger: A StringMatcher proto in route/virtual-host config with no exact/safe_regex/prefix/suffix/contains field set, or a case added in a newer Envoy API than this parser supports.

Common situations: Management server serializes an empty StringMatcher {} for a header match; config tooling drops the pattern field; client library older than the control plane's Envoy API version.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at xds/src/main/java/io/grpc/xds/internal/MatcherParser.java:107

  /** Translate StringMatcher envoy proto to internal StringMatcher. */
  public static Matchers.StringMatcher parseStringMatcher(
            io.envoyproxy.envoy.type.matcher.v3.StringMatcher proto) {
    switch (proto.getMatchPatternCase()) {
      case EXACT:
        return Matchers.StringMatcher.forExact(proto.getExact(), proto.getIgnoreCase());
      case PREFIX:
        return Matchers.StringMatcher.forPrefix(proto.getPrefix(), proto.getIgnoreCase());
      case SUFFIX:
        return Matchers.StringMatcher.forSuffix(proto.getSuffix(), proto.getIgnoreCase());
      case SAFE_REGEX:
        return Matchers.StringMatcher.forSafeRegEx(
                Pattern.compile(proto.getSafeRegex().getRegex()));
      case CONTAINS:
        return Matchers.StringMatcher.forContains(proto.getContains(), proto.getIgnoreCase());
      case MATCHPATTERN_NOT_SET:
      default:
        throw new IllegalArgumentException(
                "Unknown StringMatcher match pattern: " + proto.getMatchPatternCase());
    }
  }

  /** Translate StringMatcher xDS proto to internal StringMatcher. */
  public static Matchers.StringMatcher parseStringMatcher(
            com.github.xds.type.matcher.v3.StringMatcher proto) {
    switch (proto.getMatchPatternCase()) {
      case EXACT:
        return Matchers.StringMatcher.forExact(proto.getExact(), proto.getIgnoreCase());
      case PREFIX:
        return Matchers.StringMatcher.forPrefix(
            checkNonEmpty(proto.getPrefix(), "prefix"), proto.getIgnoreCase());
      case SUFFIX:
        return Matchers.StringMatcher.forSuffix(
            checkNonEmpty(proto.getSuffix(), "suffix"), proto.getIgnoreCase());
      case SAFE_REGEX:
        String regex = checkNonEmpty(proto.getSafeRegex().getRegex(), "regex");

View on GitHub (pinned to 64daddc1f3)