grpc/grpc-go · error
empty suffix is not allowed in StringMatcher
Error message
empty suffix is not allowed in StringMatcher
What it means
Symmetric to the prefix case: the Suffix variant of StringMatcher requires a non-empty suffix string. An empty suffix is rejected at string_matcher.go:112-113 because it would match every string and carries no information.
Solutions
- Set the suffix to a non-empty value (e.g. a domain like `.example.com`).
- If the intent was catch-all, remove the matcher instead of using an empty suffix.
- Fix the upstream config generator that produced the empty value.
Example fix
// before
sm, err := matcher.StringMatcherFromProto(&v3matcherpb.StringMatcher{
MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ""},
}) // err: empty suffix is not allowed
// after
sm, err := matcher.StringMatcherFromProto(&v3matcherpb.StringMatcher{
MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: ".example.com"},
}) Defensive patterns
Strategy: validation
Validate before calling
func validateStringMatcherProto(p *v3matcherpb.StringMatcher) error {
if p == nil { return errors.New("nil StringMatcher") }
if _, ok := p.GetMatchPattern().(*v3matcherpb.StringMatcher_Suffix); ok && p.GetSuffix() == "" {
return errors.New("StringMatcher.suffix must not be empty")
}
return nil
} Prevention
- Never emit `suffix: ""` — omit the matcher entirely for a catch-all.
- Lint RBAC/route configs for empty suffix values in CI.
- Use meaningful suffixes (e.g. domain names) and document examples for policy authors.
When it happens
Trigger: Triggered when an xDS-supplied StringMatcher sets `suffix: ""`. Surfaces during decoding of header matchers, path matchers, or other StringMatcher-typed fields.
Common situations: Defaulted empty suffix in control-plane config; YAML `suffix:` with no value; an Envoy policy that used empty suffix as a placeholder; templating or serialization that emits empty strings.
Related errors
- empty contains is not allowed in StringMatcher
- empty prefix is not allowed in StringMatcher
- input StringMatcher proto is nil
- unknown header matcher type
- gcpauthn: cache_config.cache_size must be greater than zero
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/4d8f75b597a14a0c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/matcher/string_matcher.go:113
//
// Returns a non-nil error if matcherProto is invalid.
func StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatcher, error) {
if matcherProto == nil {
return StringMatcher{}, errors.New("input StringMatcher proto is nil")
}
matcher := StringMatcher{ignoreCase: matcherProto.GetIgnoreCase()}
switch mt := matcherProto.GetMatchPattern().(type) {
case *v3matcherpb.StringMatcher_Exact:
matcher.exactMatch = newStrPtr(&mt.Exact, matcher.ignoreCase)
case *v3matcherpb.StringMatcher_Prefix:
if matcherProto.GetPrefix() == "" {
return StringMatcher{}, errors.New("empty prefix is not allowed in StringMatcher")
}
matcher.prefixMatch = newStrPtr(&mt.Prefix, matcher.ignoreCase)
case *v3matcherpb.StringMatcher_Suffix:
if matcherProto.GetSuffix() == "" {
return StringMatcher{}, errors.New("empty suffix is not allowed in StringMatcher")
}
matcher.suffixMatch = newStrPtr(&mt.Suffix, matcher.ignoreCase)
case *v3matcherpb.StringMatcher_SafeRegex:
regex := matcherProto.GetSafeRegex().GetRegex()
re, err := CompileSafeRegex(regex)
if err != nil {
return StringMatcher{}, fmt.Errorf("safe_regex matcher %q is invalid", regex)
}
matcher = NewRegexStringMatcher(re)
case *v3matcherpb.StringMatcher_Contains:
if matcherProto.GetContains() == "" {
return StringMatcher{}, errors.New("empty contains is not allowed in StringMatcher")
}
matcher.containsMatch = newStrPtr(&mt.Contains, matcher.ignoreCase)
default:
return StringMatcher{}, fmt.Errorf("unrecognized string matcher: %+v", matcherProto)
}
return matcher, nilView on GitHub (pinned to 0c51461d27)