{"record":{"id":"5d484ac232ee6520","repo":"grpc/grpc-go","slug":"invalid-header-mutation-value-for-header-key-q-e","errorCode":null,"errorMessage":"invalid header mutation: value for header key %q exceeds the maximum length of %d bytes","messagePattern":"invalid header mutation: value for header key %q exceeds the maximum length of (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extconfig.go","lineNumber":147,"sourceCode":"\t\treturn fmt.Errorf(\"input metadata is nil\")\n\t}\n\tif hmr.DisallowAll {\n\t\treturn nil\n\t}\n\n\tfor _, hvo := range hvos {\n\t\theader := hvo.GetHeader()\n\t\tkey := header.GetKey()\n\t\tif err := validateHeaderKey(key); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid header mutation: %v\", err)\n\t\t}\n\n\t\tvalue := header.GetValue()\n\t\tif strings.HasSuffix(key, \"-bin\") {\n\t\t\tvalue = string(header.GetRawValue())\n\t\t}\n\t\tif len(value) > maxHeaderSize {\n\t\t\treturn fmt.Errorf(\"invalid header mutation: value for header key %q exceeds the maximum length of %d bytes\", key, maxHeaderSize)\n\t\t}\n\t\t// ValidatePair rejects values carrying bytes outside %x20-%x7E. It\n\t\t// skips the value check for \"-bin\" keys, whose values the transport\n\t\t// base64 encodes.\n\t\tif err := imetadata.ValidatePair(key, value); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid header mutation: %v\", err)\n\t\t}\n\n\t\tif !hmr.allow(key) {\n\t\t\tif hmr.DisallowIsError {\n\t\t\t\treturn fmt.Errorf(\"header mutation disallowed by headerMutationRules for header key %q\", key)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Perform the mutation on output metadata using the append_action\n\t\t// field from the header value option.\n\t\tswitch hvo.GetAppendAction() {","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/httpfilter/extconfig.go#L129-L165","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// ext_proc server (Go) - before\nhdr := &corev3.HeaderValueOption{\n  Header: &corev3.HeaderValue{Key: \"x-blob\", Value: hugeString},\n}\n// after: cap and reject/truncate server-side\nconst maxHeaderSize = 16384\nif len(hugeString) > maxHeaderSize {\n  return status.Error(codes.InvalidArgument, \"x-blob too large\")\n}\nhdr := &corev3.HeaderValueOption{\n  Header: &corev3.HeaderValue{Key: \"x-blob\", Value: hugeString},\n}","handlingStrategy":"validation","validationCode":"// ext_proc server-side guard before adding a mutation\nconst maxHeaderSize = 16384\nfunc safeAdd(m *corev3.HeaderValueOption) error {\n  v := m.GetHeader().GetValue()\n  if strings.HasSuffix(m.GetHeader().GetKey(), \"-bin\") {\n    v = string(m.GetHeader().GetRawValue())\n  }\n  if len(v) > maxHeaderSize {\n    return fmt.Errorf(\"header %q value too large: %d > %d\", m.GetHeader().GetKey(), len(v), maxHeaderSize)\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":"// grpc-go client: ext_proc returns errors via RPC failure; handle in stream loop\nif err := procStream.Recv(); err != nil {\n  if strings.Contains(err.Error(), \"exceeds the maximum length\") {\n    log.Warn(\"ext_proc sent oversized header; check server\")\n  }\n  return err\n}","preventionTips":["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."],"tags":["grpc","xds","extproc","header-mutation","metadata"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}