{"record":{"id":"f15b95386a47c8e8","repo":"grpc/grpc-go","slug":"header-key-q-contains-illegal-characters-not-in","errorCode":null,"errorMessage":"header key %q contains illegal characters not in [0-9a-z-_.]","messagePattern":"header key %q contains illegal characters not in \\[0-9a-z-_\\.\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/metadata/metadata.go","lineNumber":117,"sourceCode":"\n// ValidateKey validates a key with the following rules (pseudo-headers are\n// skipped):\n// - the key must contain one or more characters.\n// - the characters in the key must be in [0-9 a-z _ - .].\nfunc ValidateKey(key string) error {\n\t// key should not be empty\n\tif key == \"\" {\n\t\treturn fmt.Errorf(\"there is an empty key in the header\")\n\t}\n\t// pseudo-header will be ignored\n\tif key[0] == ':' {\n\t\treturn nil\n\t}\n\t// check key, for i that saving a conversion if not using for range\n\tfor i := 0; i < len(key); i++ {\n\t\tr := key[i]\n\t\tif !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '.' && r != '-' && r != '_' {\n\t\t\treturn fmt.Errorf(\"header key %q contains illegal characters not in [0-9a-z-_.]\", key)\n\t\t}\n\t}\n\treturn nil\n}\n\n// ValidatePair validates a key-value pair with the following rules\n// (pseudo-header are skipped):\n//   - the key must contain one or more characters.\n//   - the characters in the key must be in [0-9 a-z _ - .].\n//   - if the key ends with a \"-bin\" suffix, no validation of the corresponding\n//     value is performed.\n//   - the characters in every value must be printable (in [%x20-%x7E]).\nfunc ValidatePair(key string, vals ...string) error {\n\tif err := ValidateKey(key); err != nil {\n\t\treturn err\n\t}\n\tif strings.HasSuffix(key, \"-bin\") {\n\t\treturn nil","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/metadata/metadata.go#L99-L135","documentation":"Per gRPC metadata rules, non-pseudo-header keys may only contain lowercase letters [a-z], digits [0-9], dot (.), dash (-), and underscore (_). ValidateKey at metadata.go:114-117 scans each byte and rejects any character outside that set. This enforces lowercase HTTP/2 header-name semantics and prevents illegal bytes from reaching the wire.","triggerScenarios":"Triggered when a metadata.MD key contains an uppercase letter, a space, a colon (except the leading pseudo-header colon), or any special character (e.g. ';', '/', '*', '(', etc.), and that MD is passed through metadata.Validate/ValidatePair/ValidateKey.","commonSituations":"A developer uses a mixed-case or CamelCase header name (e.g. \"Authorization\", \"X-Request-Id\"), copies an HTTP/1 header verbatim, or constructs a key from user input that contains uppercase or punctuation.","solutions":["Lowercase the key and replace disallowed characters before inserting: e.g. strings.ToLower and translate spaces/special chars to dash or underscore.","Rename offending keys to the gRPC canonical lowercase form (e.g. \"authorization\", \"x-request-id\", \"grpc-status\").","If you must carry binary/structured data, suffix the key with \"-bin\" so values are base64 and use a compliant key name.","Run metadata.Validate on your constructed MD in tests."],"exampleFix":"// before:\n//   md := metadata.Pairs(\"Authorization\", \"Bearer \"+token)\n//   ctx = metadata.NewOutgoingContext(ctx, md)\n//   // error: header key \"Authorization\" contains illegal characters\n\n// after:\n//   md := metadata.Pairs(\"authorization\", \"Bearer \"+token)\n//   ctx = metadata.NewOutgoingContext(ctx, md)","handlingStrategy":"validation","validationCode":"package main\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nfunc sanitizeKey(k string) (string, error) {\n\tk = strings.ToLower(k)\n\tfor i := 0; i < len(k); i++ {\n\t\tc := k[i]\n\t\tok := (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||\n\t\t\tc == '.' || c == '-' || c == '_'\n\t\tif !ok {\n\t\t\treturn \"\", fmt.Errorf(\"illegal char %q in key %q\", c, k)\n\t\t}\n\t}\n\treturn k, nil\n}\n\n// func main() { _, _ = sanitizeKey(\"Authorization\") }","typeGuard":null,"tryCatchPattern":"// Validate keys before adding to metadata; gRPC will reject the RPC otherwise.\n//\n//   k, err := sanitizeKey(rawKey)\n//   if err != nil { return err }\n//   md := metadata.Pairs(k, value)","preventionTips":["Always lowercase header keys before inserting into metadata.","Restrict keys to the [0-9a-z-_.] alphabet via a helper.","For binary payloads, use a '-bin' key and base64-encode the value."],"tags":["metadata","headers","validation","naming","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}