{"record":{"id":"2bfe0854a6ea56f0","repo":"grpc/grpc-go","slug":"header-key-q-contains-value-with-non-printable-as","errorCode":null,"errorMessage":"header key %q contains value with non-printable ASCII characters","messagePattern":"header key %q contains value with non-printable ASCII characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/metadata/metadata.go","lineNumber":140,"sourceCode":"\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\n\t}\n\t// check value\n\tfor _, val := range vals {\n\t\tif hasNotPrintable(val) {\n\t\t\treturn fmt.Errorf(\"header key %q contains value with non-printable ASCII characters\", key)\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":122,"sourceCodeEnd":145,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/metadata/metadata.go#L122-L145","documentation":"Returned by metadata.ValidatePair when, for a non-binary key, any value contains a byte outside the printable ASCII range %x20-%x7E. Binary headers (suffix -bin) bypass this check; ASCII headers must be printable so they can be carried as HTTP/2 header values.","triggerScenarios":"Putting raw bytes, UTF-8 multi-byte sequences, or control characters into an ASCII metadata value. Encoding binary data (e.g. a serialized proto, a UUID in raw bytes) into a non-bin header.","commonSituations":"Storing a trace context or auth blob as raw bytes in a regular header; concatenating strings that include newline/tab; copying values from a binary protocol field.","solutions":["Suffix the key with -bin to mark it as a binary header (then values are base64-encoded automatically).","Base64-encode or hex-encode binary payloads before placing them in an ASCII header.","Sanitize values to the printable ASCII range, or reject non-printable input at the boundary."],"exampleFix":"// before\nmd := metadata.Pairs(\"trace-id\", string(rawBytes)) // non-printable\n// after\nmd := metadata.Pairs(\"trace-id-bin\", string(rawBytes)) // -bin bypasses the check","handlingStrategy":"validation","validationCode":"// Choose -bin for binary payloads, validate ASCII otherwise.\nfunc addHeader(md metadata.MD, key, val string) {\n    if strings.HasSuffix(key, \"-bin\") {\n        md.Append(key, val); return\n    }\n    if !isPrintable(val) {\n        key = strings.TrimSuffix(key, \"\") + \"-bin\" // promote to binary\n    }\n    md.Append(key, val)\n}","typeGuard":"func isPrintable(s string) bool {\n    for i := 0; i < len(s); i++ { if s[i] < 0x20 || s[i] > 0x7E { return false } }\n    return true\n}","tryCatchPattern":"if err := metadata.Validate(md); err != nil {\n    if strings.Contains(err.Error(), \"non-printable\") {\n        md = encodeBinaries(md) // base64-encode offenders or rename to -bin\n    }\n}","preventionTips":["Use the -bin suffix for any non-ASCII or binary value.","Base64-encode binary blobs before placing them in headers.","Validate values at the source, not at the gRPC boundary."],"tags":["grpc","metadata","validation","headers","binary-data","encoding"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}