grpc/grpc-go · error

extproc: invalid response trailer mode

Error message

extproc: invalid response trailer mode %v: must be %q when response body mode is %q

What it means

Returned by validateBodyProcessingMode when response_body_mode is GRPC but response_trailer_mode is not SEND. grpc-go requires that, when it forwards response bodies (which include the gRPC message frames), it also forwards response trailers so the ext_proc server can observe grpc-status/grpc-message. Hit at ext_proc.go:103.

Solutions

  1. Set processing_mode.response_trailer_mode to SEND whenever response_body_mode is GRPC.
  2. If you do not want trailer forwarding, switch response_body_mode to NONE instead.
  3. Add a control-plane validation rule: response_body_mode==GRPC implies response_trailer_mode==SEND.
  4. Test the config against a grpc-go client before rolling out.

Example fix

# before (inconsistent)
processing_mode:
  response_body_mode: GRPC
  response_trailer_mode: SKIP
# after
processing_mode:
  response_body_mode: GRPC
  response_trailer_mode: SEND
Defensive patterns

Strategy: validation

Validate before calling

// control-plane guard
if cfg.ProcessingMode.GetResponseBodyMode() == v3extproc.ProcessingMode_GRPC &&
   cfg.ProcessingMode.GetResponseTrailerMode() != v3extproc.ProcessingMode_SEND {
  return fmt.Errorf("response_trailer_mode must be SEND when response_body_mode is GRPC")
}

Prevention

When it happens

Trigger: An xDS config sets processing_mode.response_body_mode=GRPC and processing_mode.response_trailer_mode to something other than SEND (or leaves it defaulted in a way that resolves non-SEND). validateBodyProcessingMode returns this error.

Common situations: Operator disables trailer forwarding to save a round trip; config partially migrated and the trailer mode field was reset; misunderstanding that GRPC body mode implies trailer forwarding.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/efbc9324a71e3b6b. Report an issue: GitHub.

Appendix: source

Thrown at internal/xds/httpfilter/extproc/ext_proc.go:104

func (builder) TypeURLs() []string {
	return []string{
		"type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExternalProcessor",
		"type.googleapis.com/envoy.extensions.filters.http.ext_proc.v3.ExtProcPerRoute",
	}
}

// validateBodyProcessingMode ensures that the body processing mode is either
// NONE or GRPC. Also ensures that if response body mode is GRPC then response
// trailer mode must be SEND.
func validateBodyProcessingMode(mode *v3procfilterpb.ProcessingMode) error {
	if m := mode.GetRequestBodyMode(); m != v3procfilterpb.ProcessingMode_NONE && m != v3procfilterpb.ProcessingMode_GRPC {
		return fmt.Errorf("extproc: invalid request body mode %v: want %q or %q", m, "NONE", "GRPC")
	}
	if m := mode.GetResponseBodyMode(); m != v3procfilterpb.ProcessingMode_NONE && m != v3procfilterpb.ProcessingMode_GRPC {
		return fmt.Errorf("extproc: invalid response body mode %v: want %q or %q", m, "NONE", "GRPC")
	}
	if mode.GetResponseBodyMode() == v3procfilterpb.ProcessingMode_GRPC && mode.GetResponseTrailerMode() != v3procfilterpb.ProcessingMode_SEND {
		return fmt.Errorf("extproc: invalid response trailer mode %v: must be %q when response body mode is %q", mode.GetResponseTrailerMode(), "SEND", "GRPC")
	}
	return nil
}

func (builder) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
	m, ok := cfg.(*anypb.Any)
	if !ok {
		return nil, fmt.Errorf("extproc: error parsing config %v: unknown type %T, want *anypb.Any", cfg, cfg)
	}
	msg := new(v3procfilterpb.ExternalProcessor)
	if err := m.UnmarshalTo(msg); err != nil {
		return nil, fmt.Errorf("extproc: failed to unmarshal config %v: %v", cfg, err)
	}
	if msg.GetProcessingMode() == nil {
		return nil, fmt.Errorf("extproc: missing processing_mode in config %v", cfg)
	}
	if err := validateBodyProcessingMode(msg.GetProcessingMode()); err != nil {
		return nil, err

View on GitHub (pinned to 0c51461d27)