grpc/grpc-go · error
header key contains value with non-printable ASCII…
Error message
header key %q contains value with non-printable ASCII characters
What it means
For non-binary metadata values (key does not end with "-bin"), every value must consist solely of printable ASCII characters in the range %x20-%x7E. ValidatePair at metadata.go:138-141 calls hasNotPrintable and rejects values containing bytes below 0x20 (control chars, newline, tab) or above 0x7E. Binary payloads must instead use a "-bin" suffixed key so they are base64-encoded.
Solutions
- If the value is binary or may contain non-printable bytes, use a key suffixed with "-bin" and attach a base64-encoded value via md.Append("my-key-bin", encodedValue).
- If the value is text, strip or escape control characters (newlines, tabs, CR) before insertion.
- Validate values with a printable-ASCII check in a helper before adding to metadata.
Example fix
// before:
// raw := someBinaryBlob // contains bytes > 0x7E
// md := metadata.Pairs("payload", string(raw))
// after:
// enc := base64.StdEncoding.EncodeToString(raw)
// md := metadata.Pairs("payload-bin", enc) Defensive patterns
Strategy: validation
Validate before calling
package main
import (
"encoding/base64"
"fmt"
)
func printableASCII(s string) bool {
for i := 0; i < len(s); i++ {
if s[i] < 0x20 || s[i] > 0x7E {
return false
}
}
return true
}
func addValue(md map[string][]string, key, val string) error {
if !printableASCII(val) {
// force the -bin channel by base64-encoding.
enc := base64.StdEncoding.EncodeToString([]byte(val))
md[key+"-bin"] = append(md[key+"-bin"], enc)
return nil
}
if !printableASCII(key) {
return fmt.Errorf("key not printable")
}
md[key] = append(md[key], val)
return nil
}
// func main() { _ = addValue } Try / catch
// Validate values before attaching; if a value may carry non-printable bytes,
// switch to a '-bin' key.
//
// if err := addValue(md, key, val); err != nil { return err } Prevention
- Use '-bin' keys (base64) for any value that may contain non-ASCII bytes.
- Strip newlines/CR/tabs from text values before insertion.
- Add a printable-ASCII assertion in your metadata-builder tests.
When it happens
Trigger: Triggered when metadata.ValidatePair is called on a non-binary key whose value contains a newline (\n), tab, NUL, or any byte outside 0x20-0x7E. Common when a developer puts raw bytes (a serialized proto, a UUID with a non-ASCII separator, a value containing a CR/LF) under a normal (non -bin) header key.
Common situations: Copying a binary blob (trace context, serialized struct, raw bytes) into a plain metadata value; a value containing a newline or control character from a log/format string; forgetting to base64-encode before attaching as metadata.
Related errors
- header key contains illegal characters not in [0-9a-z-_.]
- 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/2bfe0854a6ea56f0.
Report an issue: GitHub.
Appendix: source
Thrown at internal/metadata/metadata.go:140
// 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 nil
}
// check value
for _, val := range vals {
if hasNotPrintable(val) {
return fmt.Errorf("header key %q contains value with non-printable ASCII characters", key)
}
}
return nil
}
View on GitHub (pinned to 0c51461d27)