grpc/grpc-go · error
header key %q is in the reserved 'grpc-' space
Error message
header key %q is in the reserved 'grpc-' space
What it means
Returned by validateHeaderKey when the key has the "grpc-" prefix. The grpc-* namespace is reserved for gRPC-internal headers (grpc-trace-bin, grpc-status, grpc-message, grpc-encoding, etc.); mutating them from an external processor could corrupt the protocol. Matched by strings.HasPrefix(key, "grpc-") at extconfig.go:229.
Source
Thrown at internal/xds/httpfilter/extconfig.go:230
}
input.Delete(header)
}
return nil
}
// validateHeaderKey returns a non-nil error if key may not be mutated by an
// external processing server, either because the key is reserved or because it
// is not a valid gRPC header name.
func validateHeaderKey(key string) error {
switch {
case len(key) == 0:
return fmt.Errorf("header key is empty")
case key[0] == ':':
return fmt.Errorf("header key %q is a pseudo-header", key)
case key == "host":
return fmt.Errorf("header key %q is reserved", key)
case strings.HasPrefix(key, "grpc-"):
return fmt.Errorf("header key %q is in the reserved 'grpc-' space", key)
case key != strings.ToLower(key):
return fmt.Errorf("header key %q is not lowercase", key)
case len(key) > maxHeaderSize:
return fmt.Errorf("header key exceeds the maximum length of %d bytes", maxHeaderSize)
}
return imetadata.ValidateKey(key)
}
func (hmr *HeaderMutationRules) allow(key string) bool {
if hmr.DisallowExpr != nil && hmr.DisallowExpr.MatchString(key) {
return false
}
if hmr.AllowExpr != nil && hmr.AllowExpr.MatchString(key) {
return true
}
if hmr.AllowExpr != nil {
return false
}View on GitHub (pinned to 0c51461d27)
Solutions
- Pick a non-reserved prefix for custom headers (e.g. x-* following the extension convention).
- Never mutate grpc-trace-bin, grpc-status, grpc-message, grpc-encoding from ext_proc; use the stats handler / OpenTelemetry APIs for tracing.
- Filter out keys starting with "grpc-" when building mutations.
- If you need to propagate trace context, use the binary metadata convention under a non-reserved name.
Example fix
// before
emit("grpc-trace-bin", traceBin)
// after: use OpenTelemetry stats handler for trace propagation, not header mutation Defensive patterns
Strategy: validation
Validate before calling
// server-side: keep out of the reserved grpc- namespace
if strings.HasPrefix(key, "grpc-") { return /* skip or rename to x- */ } Prevention
- Use x-* names for custom extensions.
- Propagate trace context via OpenTelemetry stats handler, not grpc-trace-bin mutation.
- Filter grpc--prefixed keys in copy loops.
- Document the grpc- reservation in server guides.
When it happens
Trigger: The ext_proc server sends a mutation whose key starts with "grpc-" (e.g. "grpc-trace-bin", "grpc-status", or a custom "grpc-foo"). validateHeaderKey rejects it.
Common situations: Server tries to inject or rewrite tracing metadata via grpc-trace-bin; server copies all inbound headers into outbound mutations including the grpc-* transport headers; a custom server uses a grpc--prefixed name not realizing it is reserved.
Related errors
- invalid header mutation: value for header key %q exceeds the
- header mutation disallowed by headerMutationRules for header
- header mutation disallowed by headerMutationRules for header
- header key is empty
- header key %q is a pseudo-header
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/2af63daccadadd18.
Report an issue: GitHub.