grpc/grpc-go · error
invalid header mutation: value for header key %q exceeds the
Error message
invalid header mutation: value for header key %q exceeds the maximum length of %d bytes
What it means
Returned by HeaderMutationRules.ApplyAdditions when an external processing server sends a header ADD/MODIFY mutation whose value (after resolving -bin raw bytes) is longer than 16384 bytes (maxHeaderSize). The client enforces this cap on mutations it receives from the ext_proc server on the data plane, because oversized header values are not valid gRPC metadata and would break framing. The key is reported in the message; the cap is a compile-time constant.
Source
Thrown at internal/xds/httpfilter/extconfig.go:147
return fmt.Errorf("input metadata is nil")
}
if hmr.DisallowAll {
return nil
}
for _, hvo := range hvos {
header := hvo.GetHeader()
key := header.GetKey()
if err := validateHeaderKey(key); err != nil {
return fmt.Errorf("invalid header mutation: %v", err)
}
value := header.GetValue()
if strings.HasSuffix(key, "-bin") {
value = string(header.GetRawValue())
}
if len(value) > maxHeaderSize {
return fmt.Errorf("invalid header mutation: value for header key %q exceeds the maximum length of %d bytes", key, maxHeaderSize)
}
// ValidatePair rejects values carrying bytes outside %x20-%x7E. It
// skips the value check for "-bin" keys, whose values the transport
// base64 encodes.
if err := imetadata.ValidatePair(key, value); err != nil {
return fmt.Errorf("invalid header mutation: %v", err)
}
if !hmr.allow(key) {
if hmr.DisallowIsError {
return fmt.Errorf("header mutation disallowed by headerMutationRules for header key %q", key)
}
continue
}
// Perform the mutation on output metadata using the append_action
// field from the header value option.
switch hvo.GetAppendAction() {View on GitHub (pinned to 0c51461d27)
Solutions
- In the ext_proc server, chunk or drop the large value before adding it to HeaderMutation; keep each header value under 16384 bytes.
- If the value is binary, send it under a key ending in -bin so it is transported as raw bytes, and verify the decoded length still fits.
- Move large out-of-band data to the message body or a side channel instead of headers.
- If you control neither server, set failure_mode_allow so the data-plane RPC is not failed by ext_proc misbehavior (this only changes failure handling, not the validation).
Example fix
// ext_proc server (Go) - before
hdr := &corev3.HeaderValueOption{
Header: &corev3.HeaderValue{Key: "x-blob", Value: hugeString},
}
// after: cap and reject/truncate server-side
const maxHeaderSize = 16384
if len(hugeString) > maxHeaderSize {
return status.Error(codes.InvalidArgument, "x-blob too large")
}
hdr := &corev3.HeaderValueOption{
Header: &corev3.HeaderValue{Key: "x-blob", Value: hugeString},
} Defensive patterns
Strategy: validation
Validate before calling
// ext_proc server-side guard before adding a mutation
const maxHeaderSize = 16384
func safeAdd(m *corev3.HeaderValueOption) error {
v := m.GetHeader().GetValue()
if strings.HasSuffix(m.GetHeader().GetKey(), "-bin") {
v = string(m.GetHeader().GetRawValue())
}
if len(v) > maxHeaderSize {
return fmt.Errorf("header %q value too large: %d > %d", m.GetHeader().GetKey(), len(v), maxHeaderSize)
}
return nil
} Try / catch
// grpc-go client: ext_proc returns errors via RPC failure; handle in stream loop
if err := procStream.Recv(); err != nil {
if strings.Contains(err.Error(), "exceeds the maximum length") {
log.Warn("ext_proc sent oversized header; check server")
}
return err
} Prevention
- Enforce the 16384-byte cap on the ext_proc server before emitting mutations.
- Prefer -bin keys for binary data and verify decoded length.
- Add fuzz tests on the server's header construction.
- Set failure_mode_allow if you cannot control the server and need data-plane resilience.
When it happens
Trigger: An ext_proc server returns a HeaderMutation with an appended/overwritten HeaderValueOption whose value field exceeds 16 KiB. Triggered during ApplyAdditions at extconfig.go:146 while the gRPC client applies the server's response to the outgoing/incoming metadata.
Common situations: The ext_proc server tries to forward a large JWT, correlation blob, or base64-encoded payload as a single header; a server bug serializes a whole object into one header; the server was written for Envoy (higher limits) and is reused against grpc-go's stricter cap.
Related errors
- 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
- header key %q is reserved
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/5d484ac232ee6520.
Report an issue: GitHub.