grpc/grpc-go · error
external processor unexpectedly sent response trailers when
Error message
external processor unexpectedly sent response trailers when response trailer processing is disabled
What it means
Raised when the server sends response_trailers while responseTrailerMode is modeSkip. The client did not negotiate response-trailer processing, so a response-trailers message is a protocol violation and the proc stream is failed.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1333
header := resp.GetResponseHeaders()
// Check if the status in the header response is CONTINUE; if not, fail
// the stream.
if status := header.GetResponse().GetStatus(); status != v3procservicepb.CommonResponse_CONTINUE {
cs.failProcStream(fmt.Errorf("external processor returned unexpected status %v for response headers, expected %v", status, v3procservicepb.CommonResponse_CONTINUE))
return
}
if err = cs.applyMutations(header.GetResponse().GetHeaderMutation(), cs.responseHeader); err != nil {
cs.failProcStream(err)
return
}
// Signal that the response header is modified and ready to be sent to the
// client, so that if there is any buffered response body, it can be sent
// after the header.
cs.fireResponseHeadersReady()
case resp.GetResponseTrailers() != nil:
if cs.config.processingModes.responseTrailerMode == modeSkip {
cs.failProcStream(fmt.Errorf("external processor unexpectedly sent response trailers when response trailer processing is disabled"))
return
}
if !cs.trailerSent.Load() {
cs.failProcStream(fmt.Errorf("external processor sent response trailers before response trailers were sent to it"))
return
}
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()View on GitHub (pinned to 03255a9237)
Solutions
- Set processing_mode.response_trailer_mode to SEND if trailer processing is desired.
- Otherwise fix the server to not emit response_trailers when trailers are not negotiated.
- Enable failure_mode_allow to bypass during remediation.
Example fix
// before processing_mode: response_trailer_mode: SKIP # server sends response_trailers anyway // after processing_mode: response_trailer_mode: SEND
Defensive patterns
Strategy: validation
Validate before calling
// Server: suppress response_trailers when negotiated trailer mode is SKIP.
func maybeResponseTrailers(mode v3procfilterpb.ProcessingMode_HeaderSendMode, resp *pb.ProcessingResponse) *pb.ProcessingResponse {
if mode == v3procfilterpb.ProcessingMode_SKIP && resp.GetResponseTrailers() != nil {
return nil
}
return resp
} Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Set response_trailer_mode SEND iff the server will mutate trailers.
- Enable failure_mode_allow during server rollouts.
When it happens
Trigger: processing_mode.response_trailer_mode == SKIP and the server emits a ProcessingResponse.response_trailers.
Common situations: Server always mutates trailers; client left response_trailer_mode at SKIP (the default when not using GRPC body mode). Per-route override disabled trailers but shared server still emits them.
Related errors
- external processor unexpectedly sent request body when reque
- external processor unexpectedly sent response body when resp
- external processor sent response body after response trailer
- external processor unexpectedly sent response headers when r
- external processor sent response trailers before response tr
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/e333cd5c626f50a5.
Report an issue: GitHub.