grpc/grpc-go · error
rbac: error constructing matching engine
Error message
rbac: error constructing matching engine: %v
What it means
parseConfig (rbac.go:112) wraps an error from rbac.NewChainEngine, which builds the policy-matching engine from the RBAC rules. Construction can fail on malformed permissions/principals, unsupported matcher types, or invalid regex/CEL-like expressions even after A41 header-name validation passes.
Solutions
- Read the wrapped error from rbac.NewChainEngine to find the offending matcher or permission.
- Simplify the policy (remove the offending permission/principal) to isolate the failing rule.
- Align grpc-go and go-control-plane versions so the matcher library understands the policy shape.
Example fix
// before: policy references an unsupported matcher
permissions: [{ rule: { destination_port: { range: { start: 0, end: 0 } } } }]
// after: use a supported permission kind
permissions: [{ rule: { url_path: { path: { exact: "/foo" } } } }] Defensive patterns
Strategy: validation
Validate before calling
// Validate the policy compiles standalone before shipping it:
ce, err := rbac.NewChainEngine([]*v3rbacpb.RBAC{policy}, "")
if err != nil {
return fmt.Errorf("policy does not compile: %w", err)
} Try / catch
fc, err := rbacBuilder.ParseFilterConfig(anyCfg)
if err != nil && strings.Contains(err.Error(), "constructing matching engine") {
// strip permissions/principals one by one to isolate the failing rule
} Prevention
- Pre-compile RBAC policies in CI using rbac.NewChainEngine before publishing.
- Keep grpc-go and go-control-plane versions aligned.
- Avoid matcher types not yet supported by the data-plane's rbac library.
When it happens
Trigger: An RBAC policy passes the A41 condition/header-name checks but contains a permission or principal that the chain engine cannot compile — e.g., an unknown permission/principal identifier kind, an invalid regex, or a malformed matcher.
Common situations: Control-plane emits a policy that uses a matcher grpc-go does not yet support (version skew); hand-authored RBAC config with a typo'd regex; partial upgrade of the rbac matcher library.
Related errors
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/6f67689f50896f17.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/httpfilter/rbac/rbac.go:112
// Two cases where this HTTP Filter is a no op:
// "If absent, no enforcing RBAC policy will be applied" - RBAC
// Documentation for Rules field.
// "At this time, if the RBAC.action is Action.LOG then the policy will be
// completely ignored, as if RBAC was not configured." - A41
if rbacCfg.Rules == nil || rbacCfg.GetRules().GetAction() == v3rbacpb.RBAC_LOG {
return config{}, nil
}
// TODO(gregorycooke) - change the call chain to here so we have the filter
// name to input here instead of an empty string. It will come from here:
// https://github.com/grpc/grpc-go/blob/eff0942e95d93112921414aee758e619ec86f26f/xds/internal/xdsclient/xdsresource/unmarshal_lds.go#L199
ce, err := rbac.NewChainEngine([]*v3rbacpb.RBAC{rbacCfg.GetRules()}, "")
if err != nil {
// "At this time, if the RBAC.action is Action.LOG then the policy will be
// completely ignored, as if RBAC was not configured." - A41
if rbacCfg.GetRules().GetAction() != v3rbacpb.RBAC_LOG {
return nil, fmt.Errorf("rbac: error constructing matching engine: %v", err)
}
}
return config{chainEngine: ce}, nil
}
// normalizePermissionHeaders applies the A41 header-name rules to every header
// matcher reachable from permission, including those nested inside and/or/not
// rules.
func normalizePermissionHeaders(permission *v3rbacpb.Permission) error {
switch p := permission.GetRule().(type) {
case *v3rbacpb.Permission_Header:
return normalizeHeaderMatcher(p.Header)
case *v3rbacpb.Permission_AndRules:
for _, rule := range p.AndRules.GetRules() {
if err := normalizePermissionHeaders(rule); err != nil {
return err
}View on GitHub (pinned to 0c51461d27)