grpc/grpc-go · error

malformed binary metadata %q in header %q: %v

Error message

malformed binary metadata %q in header %q: %v

What it means

NewServerHandlerTransport (handler_server.go:53) decodes every incoming header; for keys ending in -bin it base64-decodes the value via decodeBinHeader. If decoding fails it writes HTTP 400 and returns this error (codes.Internal). The error names the offending value and header key.

Source

Thrown at internal/transport/handler_server.go:129

		}
		st.timeoutSet = true
		st.timeout = to
	}

	metakv := []string{"content-type", contentType}
	if r.Host != "" {
		metakv = append(metakv, ":authority", r.Host)
	}
	for k, vv := range r.Header {
		k = strings.ToLower(k)
		if isReservedHeader(k) && !isWhitelistedHeader(k) {
			continue
		}
		for _, v := range vv {
			v, err := decodeMetadataHeader(k, v)
			if err != nil {
				msg := fmt.Sprintf("malformed binary metadata %q in header %q: %v", v, k, err)
				http.Error(w, msg, http.StatusBadRequest)
				return nil, status.Error(codes.Internal, msg)
			}
			metakv = append(metakv, k, v)
		}
	}
	st.headerMD = metadata.Pairs(metakv...)

	return st, nil
}

// serverHandlerTransport is an implementation of ServerTransport
// which replies to exactly one gRPC request (exactly one HTTP request),
// using the net/http.Handler interface. This http.Handler is guaranteed
// at this point to be speaking over HTTP/2, so it's able to speak valid
// gRPC.
type serverHandlerTransport struct {
	rw         http.ResponseWriter
	req        *http.Request

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure any '-bin' header value is standard base64-encoded (the gRPC wire convention for binary metadata).
  2. If a proxy rewrites binary headers, verify it preserves/produces valid base64.
  3. Drop or correctly re-encode the offending header at the edge if it cannot be trusted.

Example fix

// before: a custom binary header set without base64
//   x-auth-bin: <raw bytes>
// server: malformed binary metadata

// after: base64-encode binary header values
import "encoding/base64"
h.Set("x-auth-bin", base64.StdEncoding.EncodeToString(rawBytes))
Defensive patterns

Strategy: validation

Validate before calling

// Validate that any -bin header is valid base64 before forwarding
import ("encoding/base64"; "strings")
func validBinHeader(k, v string) bool {
    if !strings.HasSuffix(k, "-bin") { return true }
    _, err := base64.StdEncoding.DecodeString(v)
    return err == nil
}

Try / catch

// NewServerHandlerTransport returns this as an error; respond and abort
st, err := transport.NewServerHandlerTransport(w, r, stats, pool)
if err != nil {
    // HTTP 400 already written for malformed binary metadata
    return
}

Prevention

When it happens

Trigger: An incoming request carries a header whose key ends in '-bin' (the gRPC binary-metadata convention) but whose value is not valid base64 - e.g. truncated, containing illegal characters, or wrong padding. decodeMetadataHeader (http_util.go:150) -> decodeBinHeader returns the error.

Common situations: A proxy or client sets a binary header (e.g. grpc-status-details-bin, a custom auth-bin) with incorrectly base64-encoded data; a truncated header from an MTU/transfer issue; a non-gRPC producer that didn't base64-encode binary metadata per the gRPC spec.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/76e9d4d4e893ad26. Report an issue: GitHub.