grpc/grpc-go · error

extproc: invalid request body mode %v: want %q or %q

Error message

extproc: invalid request body mode %v: want %q or %q

What it means

Raised by validateBodyProcessingMode (ext_proc.go:98) when the ExternalProcessor processing_mode.request_body_mode is anything other than NONE or GRPC. The gRPC ext_proc implementation only supports NONE (skip body) and GRPC (stream gRPC messages); other Envoy modes (STREAMED, BUFFERED, BUFFERED_PARTIAL, FULL_DUPLEX_STREAMED) are rejected during config validation, so the LDS resource is NACKed.

Source

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

func timeSince(t time.Time) time.Duration {
	return iextproc.TimeSinceFunc(t)
}

type builder struct{}

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)

View on GitHub (pinned to 03255a9237)

Solutions

  1. Set request_body_mode to NONE (no request body processing) or GRPC (per-gRPC-message body processing).
  2. Remove any STREAMED/BUFFERED/FULL_DUPLEX_STREAMED values copied from an Envoy HTTP config.
  3. Re-apply the resource and confirm the client ACKs it.

Example fix

// before
//   processing_mode: { request_body_mode: STREAMED }   // -> error 372
//
// after
//   processing_mode: { request_body_mode: GRPC }   // (or NONE)
Defensive patterns

Strategy: validation

Validate before calling

// Validate request body mode exactly like validateBodyProcessingMode (ext_proc.go:96-99).
func validateRequestBodyMode(pm *v3procfilterpb.ProcessingMode) error {
    if m := pm.GetRequestBodyMode(); m != v3procfilterpb.ProcessingMode_NONE && m != v3procfilterpb.ProcessingMode_GRPC {
        return fmt.Errorf("extproc: invalid request body mode %v: want %%q or %%q", m)
    }
    return nil
}

Prevention

When it happens

Trigger: msg.GetProcessingMode().GetRequestBodyMode() returns a value that is neither ProcessingMode_NONE nor ProcessingMode_GRPC at ext_proc.go:97.

Common situations: Reusing an Envoy-oriented ext_proc config that sets request_body_mode: STREAMED (Envoy's default-ish) without trimming it for gRPC; copying an HTTP ext_proc config verbatim into an xDS resource consumed by a gRPC client.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/15acff9d51dc3e4d. Report an issue: GitHub.