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

  1. 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).
  2. Audit and remove any compression interceptors on the server that set GrpcMessageCompressed on streamed body responses.
  3. 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

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


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)