{"record":{"id":"c4788f4aca43f45b","repo":"grpc/grpc-go","slug":"rls-grpckeybuilder-in-routelookupconfig-has-requi","errorCode":null,"errorMessage":"rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set {%+v}","messagePattern":"rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/internal/keys/builder.go","lineNumber":56,"sourceCode":"\tkbs := cfg.GetGrpcKeybuilders()\n\tif len(kbs) == 0 {\n\t\treturn nil, errors.New(\"rls: RouteLookupConfig does not contain any GrpcKeyBuilder\")\n\t}\n\n\tbm := make(map[string]builder)\n\tfor _, kb := range kbs {\n\t\t// Extract keys from `headers`, `constant_keys` and `extra_keys` fields\n\t\t// and populate appropriate values in the builder struct. Also ensure\n\t\t// that keys are not repeated.\n\t\tvar matchers []matcher\n\t\tseenKeys := make(map[string]bool)\n\t\tconstantKeys := kb.GetConstantKeys()\n\t\tfor k := range kb.GetConstantKeys() {\n\t\t\tseenKeys[k] = true\n\t\t}\n\t\tfor _, h := range kb.GetHeaders() {\n\t\t\tif h.GetRequiredMatch() {\n\t\t\t\treturn nil, fmt.Errorf(\"rls: GrpcKeyBuilder in RouteLookupConfig has required_match field set {%+v}\", kbs)\n\t\t\t}\n\t\t\tkey := h.GetKey()\n\t\t\tif seenKeys[key] {\n\t\t\t\treturn nil, fmt.Errorf(\"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q across headers, constant_keys and extra_keys {%+v}\", key, kbs)\n\t\t\t}\n\t\t\tseenKeys[key] = true\n\t\t\tmatchers = append(matchers, matcher{key: h.GetKey(), names: h.GetNames()})\n\t\t}\n\t\tif seenKeys[kb.GetExtraKeys().GetHost()] {\n\t\t\treturn nil, fmt.Errorf(\"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}\", kb.GetExtraKeys().GetHost(), kbs)\n\t\t}\n\t\tif seenKeys[kb.GetExtraKeys().GetService()] {\n\t\t\treturn nil, fmt.Errorf(\"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}\", kb.GetExtraKeys().GetService(), kbs)\n\t\t}\n\t\tif seenKeys[kb.GetExtraKeys().GetMethod()] {\n\t\t\treturn nil, fmt.Errorf(\"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q in extra_keys from constant_keys or headers {%+v}\", kb.GetExtraKeys().GetMethod(), kbs)\n\t\t}\n\t\tb := builder{","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/balancer/rls/internal/keys/builder.go#L38-L74","documentation":"The RLS (Route Lookup Service) key builder extracts fields from request metadata to form cache keys. Each header matcher in a GrpcKeyBuilder has a `required_match` field, but grpc-go's RLS implementation explicitly rejects any builder whose header sets `required_match=true` (builder.go:55-56). This matches gRFC A24/L12, where required_match is not supported for RLS key builders because all matched headers must always be included. The error is returned from keys.MakeBuilderMap() while the RLS LB policy parses its service config, so the bad config is rejected before any routing happens.","triggerScenarios":"An RLS service config is applied (via xDS, WithDefaultServiceConfig, or a service config from the name resolver) whose RouteLookupConfig.grpc_keybuilders[].headers[].required_match is true for at least one header. MakeBuilderMap iterates kb.GetHeaders() and returns this error on the first header with h.GetRequiredMatch()==true.","commonSituations":"Translating an Envoy RLS config (which supports required_match) into a gRPC client config; an xDS control plane (e.g., older Traffic Director / Istio) emitting required_match on RLS key builders; hand-written RouteLookupConfig protos copied from another data plane.","solutions":["Set required_match to false (or omit it) on every header entry inside each GrpcKeyBuilder in your RouteLookupConfig.","If the config originates from an xDS control plane, update or reconfigure the control plane so it does not set required_match on RLS key builders.","Re-test by reloading the service config; the RLS balancer will accept the builder once no header has required_match set."],"exampleFix":"// before (RouteLookupConfig proto)\nkb := &rlspb.GrpcKeyBuilder{\n  Headers: []*rlspb.GrpcKeyBuilder_Name{\"key\": \"region\", \"required_match\": true}},\n  Names: []*rlspb.GrpcKeyBuilder_Name{{Service: \"svc\", Method: \"Method\"}},\n}\n// after\nkb := &rlspb.GrpcKeyBuilder{\n  Headers: []*rlspb.GrpcKeyBuilder_Header{{Key: \"region\", Names: []string{\"x-region\"}}}, // required_match omitted\n  Names:   []*rlspb.GrpcKeyBuilder_Name{{Service: \"svc\", Method: \"Method\"}},\n}","handlingStrategy":"validation","validationCode":"func validateRLSKeyBuilders(cfg *rlspb.RouteLookupConfig) error {\n\tfor _, kb := range cfg.GetGrpcKeybuilders() {\n\t\tfor _, h := range kb.GetHeaders() {\n\t\t\tif h.GetRequiredMatch() {\n\t\t\t\treturn fmt.Errorf(\"header key %q has required_match set; not supported\", h.GetKey())\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the RouteLookupConfig with a helper before applying it as a service config or returning it from a resolver.","Never copy required_match from Envoy-style RLS configs into gRPC GrpcKeyBuilder protos.","Add a unit test that feeds your RLS config through keys.MakeBuilderMap and asserts no error."],"tags":["go","grpc","rls","config","validation","balancer"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}