grpc/grpc-go · error
header key contains illegal characters not in [0-9a-z-_.]
Error message
header key %q contains illegal characters not in [0-9a-z-_.]
What it means
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.
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.
Example fix
// before:
// md := metadata.Pairs("Authorization", "Bearer "+token)
// ctx = metadata.NewOutgoingContext(ctx, md)
// // error: header key "Authorization" contains illegal characters
// after:
// md := metadata.Pairs("authorization", "Bearer "+token)
// ctx = metadata.NewOutgoingContext(ctx, md) Defensive patterns
Strategy: validation
Validate before calling
package main
import (
"fmt"
"strings"
)
func sanitizeKey(k string) (string, error) {
k = strings.ToLower(k)
for i := 0; i < len(k); i++ {
c := k[i]
ok := (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '.' || c == '-' || c == '_'
if !ok {
return "", fmt.Errorf("illegal char %q in key %q", c, k)
}
}
return k, nil
}
// func main() { _, _ = sanitizeKey("Authorization") } Try / catch
// Validate keys before adding to metadata; gRPC will reject the RPC otherwise.
//
// k, err := sanitizeKey(rawKey)
// if err != nil { return err }
// md := metadata.Pairs(k, value) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- header key contains value with non-printable ASCII…
- there is an empty key in the header
- invalid requestHashHeader
- invalid requestHashHeader
- buffer size is not an exponent of two
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/f15b95386a47c8e8.
Report an issue: GitHub.
Appendix: source
Thrown at internal/metadata/metadata.go:117
// ValidateKey validates a key with the following rules (pseudo-headers are
// skipped):
// - the key must contain one or more characters.
// - the characters in the key must be in [0-9 a-z _ - .].
func ValidateKey(key string) error {
// key should not be empty
if key == "" {
return fmt.Errorf("there is an empty key in the header")
}
// pseudo-header will be ignored
if key[0] == ':' {
return nil
}
// check key, for i that saving a conversion if not using for range
for i := 0; i < len(key); i++ {
r := key[i]
if !(r >= 'a' && r <= 'z') && !(r >= '0' && r <= '9') && r != '.' && r != '-' && r != '_' {
return fmt.Errorf("header key %q contains illegal characters not in [0-9a-z-_.]", key)
}
}
return nil
}
// ValidatePair validates a key-value pair with the following rules
// (pseudo-header are skipped):
// - the key must contain one or more characters.
// - the characters in the key must be in [0-9 a-z _ - .].
// - if the key ends with a "-bin" suffix, no validation of the corresponding
// value is performed.
// - the characters in every value must be printable (in [%x20-%x7E]).
func ValidatePair(key string, vals ...string) error {
if err := ValidateKey(key); err != nil {
return err
}
if strings.HasSuffix(key, "-bin") {
return nilView on GitHub (pinned to 0c51461d27)