{"record":{"id":"4e3dadf8573a2e7c","repo":"grpc/grpc-go","slug":"invalid-requesthashheader-q-v","errorCode":null,"errorMessage":"invalid requestHashHeader %q: %v","messagePattern":"invalid requestHashHeader %q: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"balancer/ringhash/config.go","lineNumber":70,"sourceCode":"\t\tcfg.MaxRingSize = defaultMaxSize\n\t}\n\tif cfg.MinRingSize > cfg.MaxRingSize {\n\t\treturn nil, fmt.Errorf(\"min %v is greater than max %v\", cfg.MinRingSize, cfg.MaxRingSize)\n\t}\n\tif cfg.MinRingSize > envconfig.RingHashCap {\n\t\tcfg.MinRingSize = envconfig.RingHashCap\n\t}\n\tif cfg.MaxRingSize > envconfig.RingHashCap {\n\t\tcfg.MaxRingSize = envconfig.RingHashCap\n\t}\n\tif !envconfig.RingHashSetRequestHashKey {\n\t\tcfg.RequestHashHeader = \"\"\n\t}\n\tif cfg.RequestHashHeader != \"\" {\n\t\tcfg.RequestHashHeader = strings.ToLower(cfg.RequestHashHeader)\n\t\t// See rules in https://github.com/grpc/proposal/blob/master/A76-ring-hash-improvements.md#explicitly-setting-the-request-hash-key\n\t\tif err := metadata.ValidateKey(cfg.RequestHashHeader); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid requestHashHeader %q: %v\", cfg.RequestHashHeader, err)\n\t\t}\n\t\tif strings.HasSuffix(cfg.RequestHashHeader, \"-bin\") {\n\t\t\treturn nil, fmt.Errorf(\"invalid requestHashHeader %q: key must not end with \\\"-bin\\\"\", cfg.RequestHashHeader)\n\t\t}\n\t}\n\treturn &cfg, nil\n}\n","sourceCodeStart":52,"sourceCodeEnd":78,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/balancer/ringhash/config.go#L52-L78","documentation":"Returned by ringhash.parseConfig (config.go:69-71) when requestHashHeader (after ToLower) fails metadata.ValidateKey. Per gRFC A76, the request hash header must be a valid gRPC metadata key: lowercase alphanumeric plus '-' and '_', starting with a letter, 1-63 chars in the name part. The %q is the lowercased header and %v is the ValidateKey error.","triggerScenarios":"ring_hash config sets requestHashHeader to a string with uppercase letters (pre-lowercase), illegal characters (spaces, colons, dots), empty, or otherwise not matching the metadata key grammar.","commonSituations":"Using an HTTP header name verbatim (e.g. \"X-User-Id\", \"User Id\") instead of a valid gRPC metadata key. Only enabled when envconfig.RingHashSetRequestHashKey is true (xDS-driven), so commonly seen in xDS deployments.","solutions":["Set requestHashHeader to a valid metadata key: lowercase, alphanumeric with '-'/'_', e.g. \"user-id\".","Confirm the header you send in metadata exactly matches (case-insensitively) the configured key.","If you do not need explicit hash keys, leave requestHashHeader unset so ringhash falls back to the xDS-provided hash."],"exampleFix":"// before\nraw := `{\"requestHashHeader\": \"X-User-Id\"}` // uppercase/invalid -> error\n\n// after\nraw := `{\"requestHashHeader\": \"user-id\"}`","handlingStrategy":"validation","validationCode":"import \"google.golang.org/grpc/internal/metadata\"\n\nfunc validateRequestHashHeader(h string) error {\n    h = strings.ToLower(h)\n    return metadata.ValidateKey(h)\n}","typeGuard":"func isValidMetadataKey(h string) bool {\n    h = strings.ToLower(h)\n    if h == \"\" || len(h) > 63 { return false }\n    for _, r := range h {\n        if !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '-' && r != '_' && r != '.' {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":null,"preventionTips":["Use lowercase, simple header names for hash keys.","Match the key you actually attach to outgoing metadata.","Leave requestHashHeader unset if you don't need explicit hashing."],"tags":["go","grpc","load-balancing","ring-hash","metadata","config"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}