grpc/grpc-go · error

external processor unexpectedly sent response body when resp

Error message

external processor unexpectedly sent response body when response body processing is disabled

What it means

Raised in recvFromProcServerLoop when the server's response carries a response_body field but responseBodyMode is modeSkip. The client did not negotiate response-body processing, so a response-body mutation is a protocol violation and the proc stream is failed.

Source

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

		switch {
		case resp.GetRequestBody() != nil:
			if cs.config.processingModes.requestBodyMode == modeSkip {
				cs.failProcStream(fmt.Errorf("external processor unexpectedly sent request body when request body processing is disabled"))
				return
			}

			streamedResp, ok := cs.validateBodyResponse(resp.GetRequestBody())
			if !ok {
				return
			}
			if streamedResp.GetEndOfStream() {
				cs.discardRequests.Store(true)
			}
			cs.mutatedReqBuffer.Put(streamedResp)

		case resp.GetResponseBody() != nil:
			if cs.config.processingModes.responseBodyMode == modeSkip {
				cs.failProcStream(fmt.Errorf("external processor unexpectedly sent response body when response body processing is disabled"))
				return
			}

			// If response headers have been sent and mutated response headers have
			// not been received before receiving the response body message, fail the
			// RPC.
			if cs.config.processingModes.responseHeaderMode == modeSend && !cs.responseHeadersReady.HasFired() {
				cs.failProcStream(fmt.Errorf("external processor sent response body before sending response headers"))
				return
			}

			// If mutated response trailers have been received before receiving the
			// response body message, fail the RPC.
			if cs.config.processingModes.responseTrailerMode == modeSend && cs.responseTrailerReady.HasFired() {
				cs.failProcStream(fmt.Errorf("external processor sent response body after response trailers were already processed"))
				return
			}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Set processing_mode.response_body_mode to GRPC if response-body processing is desired.
  2. Otherwise, correct the server so it never sets response_body when the negotiated ProtocolConfiguration has ResponseBodyMode NONE.
  3. Enable failure_mode_allow to bypass the processor on violation while you remediate the server.

Example fix

// before
processing_mode:
  response_body_mode: NONE   # server returns response_body anyway

// after
processing_mode:
  response_body_mode: GRPC
Defensive patterns

Strategy: validation

Validate before calling

// Server-side guard: suppress response_body when mode is NONE.
func maybeResponseBody(mode v3procfilterpb.ProcessingMode_BodySendMode, resp *pb.ProcessingResponse) *pb.ProcessingResponse {
    if mode == v3procfilterpb.ProcessingMode_NONE && resp.GetResponseBody() != nil {
        return nil
    }
    return resp
}

Try / catch

// Client side: failure_mode_allow -> bypass on violation.

Prevention

When it happens

Trigger: An ext-proc server sends a ProcessingResponse.response_body while processing_mode.response_body_mode is NONE. failProcStream decides teardown (failure_mode_allow off) or bypass (on).

Common situations: Server blindly mutates response bodies; client left response_body_mode at the NONE default. Per-route override disabled body mode but the shared server implementation still emits response_body.

Related errors


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