grpc/grpc-go · error

external processor returned unexpected status %v for body re

Error message

external processor returned unexpected status %v for body response, expected %v

What it means

When validating a body response from the extproc server, the filter requires the CommonResponse status to be CONTINUE (ext_proc.go:1357). Any other status value means the server is not cleanly continuing the stream; the filter refuses to apply the body mutation and fails the proc stream.

Source

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

			if cs.responseTrailerReady.HasFired() {
				cs.failProcStream(fmt.Errorf("external processor unexpectedly sent duplicate response trailers after response trailers were already processed"))
				return
			}
			trailer := resp.GetResponseTrailers()
			if err = cs.applyMutations(trailer.GetHeaderMutation(), cs.responseTrailers); err != nil {
				cs.failProcStream(err)
				return
			}
			// 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
	}

View on GitHub (pinned to 03255a9237)

Solutions

  1. Ensure the extproc server sets response.status = CONTINUE on every body ProcessingResponse it sends.
  2. If the server intends to reject, it should use an ImmediateResponse rather than a non-CONTINUE body status.
  3. Enable failure_mode_allow to tolerate the server error while debugging.

Example fix

// before
bodyResp.Response.Status = procservicepb.CommonResponse_REPLY

// after
bodyResp.Response.Status = procservicepb.CommonResponse_CONTINUE
Defensive patterns

Strategy: fallback

Validate before calling

// Server side: assert before sending
if bodyResp.GetResponse().GetStatus() != procservicepb.CommonResponse_CONTINUE {
    bodyResp.Response.Status = procservicepb.CommonResponse_CONTINUE
}

Prevention

When it happens

Trigger: The external processor returns a ProcessingResponse with a body response whose CommonResponse.Status is anything other than CONTINUE (e.g. an unset/zero status or a custom enum value).

Common situations: Extproc server sets status incorrectly on body messages; server code path that constructs body responses without setting Status=CONTINUE; protobuf version drift exposing a new status enum value the client rejects.

Related errors


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