grpc/grpc-go · error
external processor sent response body before sending respons
Error message
external processor sent response body before sending response headers
What it means
Raised when the server sends a response_body before the client has delivered (and the server has acknowledged) response headers. Because responseHeaderMode is modeSend, the filter requires response headers to be exchanged first; a response-body message arriving while responseHeadersReady has not fired is out of order and fails the proc stream.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1280
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
}
streamedResp, ok := cs.validateBodyResponse(resp.GetResponseBody())
if !ok {
return
}
if streamedResp.GetEndOfStream() {
cs.failProcStream(fmt.Errorf("external processor unexpectedly set end of stream in response body mutation"))
return
}View on GitHub (pinned to 03255a9237)
Solutions
- Fix the server to always emit its response_headers response (status CONTINUE) before any response_body, matching the negotiated order.
- If the server has nothing to mutate on headers, still return an empty response_headers with status CONTINUE once headers arrive.
- Enable failure_mode_allow so the RPC survives the violation by bypassing.
Example fix
// before: server sends body first
func Process(stream) {
headers := <-stream.Recv() // ignored
stream.Send(&ProcessingResponse{ResponseBody: ...}) // headers ack skipped -> error
}
// after: ack headers, then body
func Process(stream) {
<-stream.Recv() // response headers
stream.Send(&ProcessingResponse{ResponseHeaders:{Response:{Status:CONTINUE}}})
body := <-stream.Recv()
stream.Send(&ProcessingResponse{ResponseBody: ...})
} Defensive patterns
Strategy: validation
Validate before calling
// Server: track whether response headers were acknowledged before sending body. headersAcked := false // only set headersAcked=true after sending response_headers; gate body sends on it.
Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Server must send response_headers (CONTINUE) before any response_body.
- Serialize ProcessingResponse sends to avoid reordering.
When it happens
Trigger: processing_mode.response_header_mode == SEND and the ext-proc server emits a response_body message before returning its response_headers response, i.e. it skipped/collided the header step.
Common situations: Server bug that sends body mutation eagerly without waiting to receive response headers, or a server that only intends to mutate bodies but the client configured response_header_mode SEND expecting an explicit header ack first.
Related errors
- external processor sent response headers before response hea
- external processor unexpectedly sent duplicate response head
- external processor sent response body after response trailer
- external processor unexpectedly sent response headers when r
- external processor returned unexpected status %v for respons
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/40146c288cecd680.
Report an issue: GitHub.