grpc/grpc-go · error
unknown header matcher type
Error message
unknown header matcher type
What it means
`newHeaderMatcher` (internal/xds/rbac/matchers.go:297) switches over the `HeaderMatchSpecifier` oneof of an Envoy HeaderMatcher proto. It handles exact, safe_regex, range, present, prefix, suffix, contains, and string_match variants. The default branch at line 324-325 returns this error when the oneof is unset or holds a variant unknown to gRPC.
Solutions
- Inspect the offending HeaderMatcher proto in the xDS response to identify which (or whether) a match variant is set.
- Replace the unsupported/empty header matcher with one of the supported variants (exact, prefix, suffix, contains, present, range, safe_regex, string_match).
- If a genuinely new Envoy matcher type is required, check for a newer grpc-go release that supports it; otherwise exclude that policy from gRPC-targeted configs.
- Ensure the header matcher is fully populated (the oneof is not left unset) in the control-plane-generated config.
Example fix
// before (HeaderMatcher with no variant / unsupported variant)
hm := &route_componentspb.HeaderMatcher{Name: "x-foo"} // HeaderMatchSpecifier == nil
m, err := newHeaderMatcher(hm) // err: unknown header matcher type
// after
hm := &route_componentspb.HeaderMatcher{
Name: "x-foo",
HeaderMatchSpecifier: &route_componentspb.HeaderMatcher_ExactMatch{ExactMatch: "bar"},
}
m, err := newHeaderMatcher(hm) Defensive patterns
Strategy: type-guard
Validate before calling
// Validate a HeaderMatcher proto before it reaches newHeaderMatcher.
func validateHeaderMatcher(hm *route_componentspb.HeaderMatcher) error {
if hm == nil || hm.HeaderMatchSpecifier == nil {
return fmt.Errorf("header matcher %q has no match variant set", hm.GetName())
}
switch hm.HeaderMatchSpecifier.(type) {
case *route_componentspb.HeaderMatcher_ExactMatch,
*route_componentspb.HeaderMatcher_SafeRegexMatch,
*route_componentspb.HeaderMatcher_RangeMatch,
*route_componentspb.HeaderMatcher_PresentMatch,
*route_componentspb.HeaderMatcher_PrefixMatch,
*route_componentspb.HeaderMatcher_SuffixMatch,
*route_componentspb.HeaderMatcher_ContainsMatch,
*route_componentspb.HeaderMatcher_StringMatch:
return nil
default:
return fmt.Errorf("header matcher %q uses an unsupported type %T", hm.GetName(), hm.HeaderMatchSpecifier)
}
} Type guard
// Type-switch helper to confirm a HeaderMatcher variant is one gRPC understands.
func isSupportedHeaderMatcher(hm *route_componentspb.HeaderMatcher) bool {
switch hm.HeaderMatchSpecifier.(type) {
case *route_componentspb.HeaderMatcher_ExactMatch,
*route_componentspb.HeaderMatcher_SafeRegexMatch,
*route_componentspb.HeaderMatcher_RangeMatch,
*route_componentspb.HeaderMatcher_PresentMatch,
*route_componentspb.HeaderMatcher_PrefixMatch,
*route_componentspb.HeaderMatcher_SuffixMatch,
*route_componentspb.HeaderMatcher_ContainsMatch,
*route_componentspb.HeaderMatcher_StringMatch:
return true
}
return false
} Prevention
- Always set a concrete HeaderMatchSpecifier variant when authoring RBAC/route policies.
- Add a CI linter that rejects HeaderMatcher blocks with no variant or unknown variants for gRPC consumers.
- Track the gRPC-supported Envoy matcher set and pin control-plane versions that emit only those variants.
When it happens
Trigger: Triggered when an RBAC policy (or any xDS resource using HeaderMatcher) contains a header matcher whose `HeaderMatchSpecifier` is either nil (no variant selected) or a newer Envoy variant that gRPC has not implemented. The error is returned during RBAC chain-engine construction, which NACKs the resource.
Common situations: A control-plane/Envoy version that emits a header matcher type gRPC does not yet support; a malformed header matcher with no variant set (default-constructed proto); using Envoy-specific extensions in a policy delivered to gRPC; version skew between Envoy proto definitions and the gRPC-vendored ones.
Related errors
- ALTS: untrusted platform. ALTS is only supported on GCP
- client-side auth info is not of type alts.AuthInfo
- empty contains is not allowed in StringMatcher
- empty prefix is not allowed in StringMatcher
- empty suffix is not allowed in StringMatcher
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/558b893153b8a198.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/rbac/matchers.go:325
m = internalmatcher.NewHeaderRegexMatcher(headerMatcherConfig.Name, regex, headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_RangeMatch:
m = internalmatcher.NewHeaderRangeMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetRangeMatch().Start, headerMatcherConfig.GetRangeMatch().End, headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_PresentMatch:
m = internalmatcher.NewHeaderPresentMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPresentMatch(), headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_PrefixMatch:
m = internalmatcher.NewHeaderPrefixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetPrefixMatch(), headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_SuffixMatch:
m = internalmatcher.NewHeaderSuffixMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetSuffixMatch(), headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_ContainsMatch:
m = internalmatcher.NewHeaderContainsMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetContainsMatch(), headerMatcherConfig.InvertMatch)
case *v3route_componentspb.HeaderMatcher_StringMatch:
sm, err := internalmatcher.StringMatcherFromProto(headerMatcherConfig.GetStringMatch())
if err != nil {
return nil, fmt.Errorf("invalid string matcher %+v: %v", headerMatcherConfig.GetStringMatch(), err)
}
m = internalmatcher.NewHeaderStringMatcher(headerMatcherConfig.Name, sm, headerMatcherConfig.InvertMatch)
default:
return nil, errors.New("unknown header matcher type")
}
return &headerMatcher{matcher: m}, nil
}
func (hm *headerMatcher) match(data *rpcData) bool {
return hm.matcher.Match(data.md)
}
// urlPathMatcher matches on the URL Path of the incoming RPC. In gRPC, this
// logically maps to the full method name the RPC is calling on the server side.
// urlPathMatcher implements the matcher interface.
type urlPathMatcher struct {
stringMatcher internalmatcher.StringMatcher
}
func newURLPathMatcher(pathMatcher *v3matcherpb.PathMatcher) (*urlPathMatcher, error) {
stringMatcher, err := internalmatcher.StringMatcherFromProto(pathMatcher.GetPath())
if err != nil {View on GitHub (pinned to 0c51461d27)