grpc/grpc-go · error
external processor sent response body after response…
Error message
external processor sent response body after response trailers were already processed
What it means
Raised by recvFromProcServerLoop (ext_proc.go:1453) when responseTrailerMode is SEND and the server sends response_body after the response trailers have already been processed (responseTrailerReady has fired). Once trailers are done, response body is out of order; failProcStream fails the RPC unless failure_mode_allow bypasses it.
Solutions
- On the server, stop sending response_body once you have emitted the response trailers for the stream.
- Ensure the server processes events in order (headers -> body -> trailers) and does not interleave them.
- Enable failure_mode_allow so the dataplane RPC survives the violation.
- Add a per-stream phase machine in the server handler so body mutations cannot be sent post-trailers.
Example fix
// before: server flushes a stray response body mutation after trailers stream.Send(respTrailers) stream.Send(respBody) // late, triggers the error // after: no response body after trailers stream.Send(respBody) stream.Send(respTrailers)
Defensive patterns
Strategy: fallback
Validate before calling
// On the ext_proc SERVER: track trailer completion and forbid later body sends.
type streamState struct{ trailersSent bool }
func (s *streamState) canSendResponseBody() bool { return !s.trailersSent } Try / catch
filter.failure_mode_allow = true
if st, ok := status.FromError(err); ok && st.Code() == codes.Internal &&
strings.Contains(st.Message(), "response body after response trailers") {
// server emitted response body after trailers were already processed
} Prevention
- Server: never send response_body after you have sent response_trailers for the stream.
- Implement a per-stream phase machine (headers -> body -> trailers).
- Enable failure_mode_allow so late body mutations degrade gracefully.
- Add a server unit test that asserts no body mutation follows trailers.
When it happens
Trigger: Triggered when response_trailer_mode == SEND, the client has already forwarded and received mutations for response trailers (responseTrailerReady fired), and the server then sends another response_body (ext_proc.go:1436).
Common situations: Server handler that does not track its own phase and emits late response body mutations after trailers, or a buggy server sending buffered mutations out of order near stream end.
Related errors
- external processor sent response body before sending…
- external processor sent response trailers before response…
- external processor sent response headers before response…
- external processor unexpectedly sent duplicate response…
- external processor unexpectedly sent response body when…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/0f0b56d1dd2ee633.
Report an issue: GitHub.
Appendix: source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1453
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
}
cs.mutatedRespBuffer.Put(streamedResp)
case resp.GetResponseHeaders() != nil:
if cs.config.processingModes.responseHeaderMode == modeSkip {
cs.failProcStream(fmt.Errorf("external processor unexpectedly sent response headers when response header processing is disabled"))
return
}View on GitHub (pinned to 0c51461d27)