grpc/grpc-go · error
external processor unexpectedly set end of stream in respons
Error message
external processor unexpectedly set end of stream in response body mutation
What it means
Raised when the server's streamed response-body mutation sets EndOfStream=true. End of stream on the dataplane is driven by the dataplane itself, not by the ext-proc server, so a server-supplied EOS inside a streamed response body is treated as a protocol violation and fails the proc stream.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1296
// 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
}
if !cs.responseHeaderSent.Load() {
cs.failProcStream(fmt.Errorf("external processor sent response headers before response headers were sent to it"))
return
}
if cs.responseHeadersReady.HasFired() {
cs.failProcStream(fmt.Errorf("external processor unexpectedly sent duplicate response headers after response headers were already processed"))
return
}
View on GitHub (pinned to 03255a9237)
Solutions
- Stop setting end_of_stream on streamed_response messages from the server.
- Let the dataplane drive stream completion; the server should simply stop sending body messages when done.
- Enable failure_mode_allow to bypass during remediation.
Example fix
// before (server)
stream.Send(&pb.ProcessingResponse{ResponseBody:&pb.BodyResponse{Response:&pb.CommonResponse{BodyMutation:&pb.BodyMutation{BodyMutation:&pb.BodyMutation_StreamedResponse{StreamedResponse:&pb.StreamedBodyResponse{Body: chunk, EndOfStream: true}}}}}})
// after
stream.Send(&pb.ProcessingResponse{ResponseBody:&pb.BodyResponse{Response:&pb.CommonResponse{BodyMutation:&pb.BodyMutation{BodyMutation:&pb.BodyMutation_StreamedResponse{StreamedResponse:&pb.StreamedBodyResponse{Body: chunk}}}}}}) Defensive patterns
Strategy: validation
Validate before calling
// Server: never set EndOfStream on streamed responses.
resp := &pb.StreamedBodyResponse{Body: chunk} // EndOfStream left false Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Do not copy Envoy's EOS-in-body pattern into a gRPC ext-proc server.
- Let the dataplane drive end-of-stream.
When it happens
Trigger: validateBodyResponse receives a StreamedBodyResponse with EndOfStream set to true from the server's response_body.body_mutation.streamed_response.
Common situations: Server copied an Envoy pattern (where EOS in body mutation is allowed) into a gRPC-extproc implementation without reading the gRPC contract. Server attempts to signal end-of-response via EOS.
Related errors
- external processor unexpectedly sent response body when resp
- external processor unexpectedly sent request body when reque
- external processor sent response body before sending respons
- external processor sent response body after response trailer
- external processor unexpectedly sent response headers when r
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/2a9fcb2613cb967b.
Report an issue: GitHub.