grpc/grpc-java · error · IllegalArgumentException
HeaderMatcher [] contains malformed safe regex pattern:
Error message
HeaderMatcher [] contains malformed safe regex pattern:
What it means
MatcherParser.parseHeaderMatcher compiles the Envoy HeaderMatcher's safe_regex pattern with java.util.regex.Pattern.compile. If the regex is syntactically invalid (PatternSyntaxException), it wraps the failure into an IllegalArgumentException naming the header and the underlying regex error. The xDS config contained a regex this JVM cannot compile.
Solutions
- Fix the regex in the xDS management server config so it is valid java.util.regex syntax
- Test the pattern with Pattern.compile before deploying it to the control plane
- Read the embedded PatternSyntaxException message (it pinpoints index and problem)
- Avoid RE2/Go-only constructs; use Java-compatible equivalents
Example fix
// xDS config before
{"name":"x-tenant","safe_regex_match":{"regex":"(?P<tenant>.*)"}}
// after (named groups unsupported in older Java regex handling here)
{"name":"x-tenant","safe_regex_match":{"regex":".*"}} Defensive patterns
Strategy: validation
Validate before calling
try { java.util.regex.Pattern.compile(regex); } catch (java.util.regex.PatternSyntaxException e) { /* reject before deploying to control plane */ } Type guard
null
Try / catch
try { matcher = MatcherParser.parseHeaderMatcher(proto); }
catch (IllegalArgumentException e) { log.error("Bad header regex in route config", e); failConfigLoad(); } Prevention
- Validate regexes with Pattern.compile at config-authoring time
- Avoid RE2/Go-only regex syntax
- Pin the Envoy API version between control plane and client
- Test route configs in staging before rollout
When it happens
Trigger: An LDS/RDS route configuration contains a header_match with safe_regex_match whose regex string fails java.util.regex compilation (e.g. invalid escapes, unbalanced groups, RE2-only syntax).
Common situations: Regexes authored for RE2/other engines (e.g. possessive quantifiers or \p{...} differences) pasted into Envoy config; hand-edited control-plane config with a typo; control plane emitting Go-regexp syntax not valid in Java.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- StringMatcher (match_pattern) must be non-empty
- Unknown header matcher type:
- Unknown StringMatcher match pattern:
- Invalid address: Empty address is not allowed.
- Invalid allowed_grpc_services config for
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/16c49fdb5156fc22.
Report an issue: GitHub.
Appendix: source
Thrown at xds/src/main/java/io/grpc/xds/internal/MatcherParser.java:51
}
/** Translates envoy proto HeaderMatcher to internal HeaderMatcher.*/
public static Matchers.HeaderMatcher parseHeaderMatcher(
io.envoyproxy.envoy.config.route.v3.HeaderMatcher proto) {
switch (proto.getHeaderMatchSpecifierCase()) {
case EXACT_MATCH:
@SuppressWarnings("deprecation") // gRFC A63: support indefinitely
String exactMatch = proto.getExactMatch();
return Matchers.HeaderMatcher.forExactValue(
proto.getName(), exactMatch, proto.getInvertMatch());
case SAFE_REGEX_MATCH:
@SuppressWarnings("deprecation") // gRFC A63: support indefinitely
String rawPattern = proto.getSafeRegexMatch().getRegex();
Pattern safeRegExMatch;
try {
safeRegExMatch = Pattern.compile(rawPattern);
} catch (PatternSyntaxException e) {
throw new IllegalArgumentException(
"HeaderMatcher [" + proto.getName() + "] contains malformed safe regex pattern: "
+ e.getMessage());
}
return Matchers.HeaderMatcher.forSafeRegEx(
proto.getName(), safeRegExMatch, proto.getInvertMatch());
case RANGE_MATCH:
Matchers.HeaderMatcher.Range rangeMatch = Matchers.HeaderMatcher.Range.create(
proto.getRangeMatch().getStart(), proto.getRangeMatch().getEnd());
return Matchers.HeaderMatcher.forRange(
proto.getName(), rangeMatch, proto.getInvertMatch());
case PRESENT_MATCH:
return Matchers.HeaderMatcher.forPresent(
proto.getName(), proto.getPresentMatch(), proto.getInvertMatch());
case PREFIX_MATCH:
@SuppressWarnings("deprecation") // gRFC A63: support indefinitely
String prefixMatch = proto.getPrefixMatch();
return Matchers.HeaderMatcher.forPrefix(
proto.getName(), prefixMatch, proto.getInvertMatch());View on GitHub (pinned to 64daddc1f3)