grpc/grpc-go · error
external processor returned compressed grpc message which…
Error message
external processor returned compressed grpc message which is not supported
What it means
The extproc filter validates body responses from an external gRPC processor. In validateBodyResponse (ext_proc.go:1532), if the streamed body mutation carries GrpcMessageCompressed == true, grpc-go rejects it: the client filter does not implement gRPC-message-level decompression for streamed body responses. The stream is then torn down (or bypassed) via failProcStream.
Solutions
- Disable gRPC message compression on the external processor server for the extproc service (do not call grpc.UseCompressor, or register the service with a none-compression server option).
- Audit and remove any compression interceptors on the server that set GrpcMessageCompressed on streamed body responses.
- If compression is required for transport, switch the body mutation to a non-streamed form or move the data into a header the filter can carry uncompressed.
Example fix
// before (server side) grpc.NewServer(grpc.UnaryInterceptor(compressInterceptor)) // compresses everything including extproc // after grpc.NewServer() // extproc service sends uncompressed streamed responses
Defensive patterns
Strategy: validation
Validate before calling
// Server-side: before sending a streamed body response, ensure the flag is clear
resp := &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_ResponseBody{ResponseBody: &procpb.BodyResponse{Response: &procpb.CommonResponse{BodyMutation: &procpb.BodyMutation{Mutation: &procpb.BodyMutation_StreamedResponse{StreamedResponse: &procpb.StreamedBodyResponse{Body: chunk, GrpcMessageCompressed: false}}}}}}}
_ = resp Try / catch
// Client-side: the filter fails the stream with codes.Internal wrapping this message.
// Catch the RPC error and surface it; if failureModeAllow is true the stream is bypassed.
stream, err := client.SomeMethod(ctx)
if err != nil {
if st, ok := status.FromError(err); ok && st.Code() == codes.Internal && strings.Contains(st.Message(), "compressed grpc message") {
// external processor misbehaved; retry without extproc or alert the server owner
}
} Prevention
- Do not register compression interceptors on the extproc gRPC server.
- Audit grpc.UseCompressor usage in the external processor binary.
- Add a server-side unit test asserting GrpcMessageCompressed is false on streamed body responses.
When it happens
Trigger: The external processor returns a ProcessingResponse whose BodyResponse.BodyMutation.StreamedResponse has GrpcMessageCompressed set true. Triggered on the body phase of request or response when the server applies message compression to that streamed mutation.
Common situations: External processor server built on a framework that auto-compresses large gRPC messages, or a server-side grpc.UseCompressor("gzip") / compression interceptor applied to the extproc service. Operator enabled compression globally without excluding the extproc service.
Related errors
- external processor returned an unexpected message type %T…
- external processor returned unexpected status
- external processor sent an immediate response but immediate…
- external processor returned invalid body mutation in body…
- external processor returned unexpected status
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/6de6bbfff7240dff.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1533
// 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)
}
// closeProcSend gracefully half-closes the external processor stream across the
// background send goroutine by pushing a nil sentinel onto procSendCh.
func (cs *clientStream) closeProcSend() {View on GitHub (pinned to 0c51461d27)