{"record":{"id":"83e766f77d402a64","repo":"grpc/grpc-go","slug":"rls-grpckeybuilder-in-routelookupconfig-contains","errorCode":null,"errorMessage":"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q across headers, constant_keys and extra_keys {%+v}","messagePattern":"rls: GrpcKeyBuilder in RouteLookupConfig contains repeated key %q across headers, constant_keys and extra_keys (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/rls/internal/keys/builder.go","lineNumber":60,"sourceCode":"\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{\n\t\t\theaderKeys:   matchers,\n\t\t\tconstantKeys: constantKeys,\n\t\t\thostKey:      kb.GetExtraKeys().GetHost(),\n\t\t\tserviceKey:   kb.GetExtraKeys().GetService(),","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/rls/internal/keys/builder.go#L42-L78","documentation":"RLS assembles a single key map per request from three sources — headers, constant_keys, and (implicitly) extra_keys — and the same logical key name must not appear twice or the assembled map would collide. MakeBuilderMap tracks every key in a seenKeys set as it walks constant_keys then headers, and returns this error the moment a header's key is already in the set.","triggerScenarios":"A GrpcKeyBuilder where a header's key string equals a constant_keys key, or equals another header's key. E.g. constant_keys {\"env\":\"prod\"} plus headers [{\"key\":\"env\",\"names\":[\"x-env\"]}].","commonSituations":"Building key builders incrementally and reusing a key name; merging two builders that each define the same key; intending constant vs. header variants but using one shared key name.","solutions":["Give each logical key a unique name across headers and constant_keys (and extra_keys).","If two sources should feed one RLS key, pick one source and drop the other.","Deduplicate with a config lint that collects all keys per builder and asserts uniqueness."],"exampleFix":"// before\n\"constant_keys\": {\"env\":\"prod\"},\n\"headers\":   [{\"key\":\"env\",\"names\":[\"x-env\"]}]\n// after\n\"constant_keys\": {\"env\":\"prod\"},\n\"headers\":   [{\"key\":\"region\",\"names\":[\"x-region\"]}]","handlingStrategy":"validation","validationCode":"func validateUniqueKeys(kb *rlspb.GrpcKeyBuilder) error {\n    seen := map[string]bool{}\n    for k := range kb.GetConstantKeys() {\n        if seen[k] { return fmt.Errorf(\"dup key %q\", k) }\n        seen[k] = true\n    }\n    for _, h := range kb.GetHeaders() {\n        k := h.GetKey()\n        if seen[k] { return fmt.Errorf(\"dup key %q across headers/constant_keys\", k) }\n        seen[k] = true\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Collect every key (constant + headers + extra) per builder and assert uniqueness.","Give header-derived and constant-derived keys distinct names by convention.","Lint key builders in CI before publishing the service config."],"tags":["rls","service-config","config-validation","go","grpc","key-builder"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}