grpc/grpc-go · error
external processor returned invalid body mutation in body…
Error message
external processor returned invalid body mutation in body response
What it means
Raised by validateBodyResponse (ext_proc.go:1529) when the body mutation in a body response is not the streamed_response variant (GetBodyMutation().GetStreamedResponse() is nil). This ext_proc implementation only supports streamed gRPC body mutations; other body_mutation oneofs are rejected. failProcStream fails the RPC unless failure_mode_allow bypasses it.
Solutions
- On the server, wrap body bytes in BodyMutation.StreamedResponse{Body: bytes} rather than BodyMutation.Body.
- Enable failure_mode_allow so the client bypasses ext_proc when the server uses an unsupported mutation shape.
- Regenerate/validate your server's BodyResponse construction against the gRPC-Go ext_proc subset.
- Add a server-side helper buildBodyMutation(bytes) that always returns the streamed_response oneof.
Example fix
// before: server uses the plain body oneof (unsupported here)
BodyMutation: &procpb.BodyMutation{BodyMutation: &procpb.BodyMutation_Body{Body: chunk}}
// after: use the streamed_response oneof
BodyMutation: &procpb.BodyMutation{BodyMutation: &procpb.BodyMutation_StreamedResponse{
StreamedResponse: &procpb.StreamedBodyResponse{Body: chunk},
}} Defensive patterns
Strategy: fallback
Validate before calling
// On the ext_proc SERVER: build body mutations using the streamed_response oneof
// supported by the gRPC-Go ext_proc client.
func buildBodyMutation(chunk []byte) *procpb.BodyMutation {
return &procpb.BodyMutation{BodyMutation: &procpb.BodyMutation_StreamedResponse{
StreamedResponse: &procpb.StreamedBodyResponse{Body: chunk},
}}
} Try / catch
filter.failure_mode_allow = true
if st, ok := status.FromError(err); ok && st.Code() == codes.Internal &&
strings.Contains(st.Message(), "invalid body mutation in body response") {
// server used an unsupported body_mutation oneof (e.g. plain body bytes)
} Prevention
- Server: always wrap body bytes in BodyMutation.StreamedResponse{Body: ...}; do not use the plain Body oneof.
- Provide a single buildBodyMutation helper and use it everywhere.
- Enable failure_mode_allow so unsupported mutation shapes degrade gracefully.
- Validate server responses against the gRPC-Go ext_proc subset in CI.
When it happens
Trigger: Triggered when the server's body response sets body_mutation to the raw bytes variant (BodyMutation.Body) instead of BodyMutation.StreamedResponse, or leaves body_mutation unset (ext_proc.go:1527-1528).
Common situations: Server follows the full Envoy ext_proc spec (which allows the plain body oneof) rather than the gRPC-Go subset, a handler that writes body_mutation.body directly, or a copy-paste from an Envoy filter example.
Related errors
- external processor returned unexpected status
- external processor returned unexpected status
- external processor sent response body after response…
- external processor sent response body before sending…
- external processor sent response headers before response…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/6e5660e077e03e0c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1529
if err = cs.applyMutations(trailer.GetHeaderMutation(), cs.responseTrailers); err != nil {
cs.failProcStream(err)
return
}
// Signal that the response trailer is modified and ready to be sent to
// the client.
cs.fireResponseTrailerReady()
}
}
}
func (cs *clientStream) validateBodyResponse(bodyResp *v3procservicepb.BodyResponse) (*v3procservicepb.StreamedBodyResponse, bool) {
if status := bodyResp.GetResponse().GetStatus(); status != v3procservicepb.CommonResponse_CONTINUE {
cs.failProcStream(fmt.Errorf("external processor returned unexpected status %v for body response, expected %v", status, v3procservicepb.CommonResponse_CONTINUE))
return nil, false
}
streamedResp := bodyResp.GetResponse().GetBodyMutation().GetStreamedResponse()
if streamedResp == nil {
cs.failProcStream(fmt.Errorf("external processor returned invalid body mutation in body response"))
return nil, false
}
if streamedResp.GetGrpcMessageCompressed() {
cs.failProcStream(fmt.Errorf("external processor returned compressed grpc message which is not supported"))
return nil, false
}
return streamedResp, true
}
func (cs *clientStream) applyMutations(mutation *v3procservicepb.HeaderMutation, md metadata.MD) error {
if mutation == nil {
return nil
}
if err := cs.config.mutationRules.ApplyAdditions(mutation.GetSetHeaders(), md); err != nil {
return err
}
return cs.config.mutationRules.ApplyRemovals(mutation.GetRemoveHeaders(), md)
}View on GitHub (pinned to 0c51461d27)