grpc/grpc-go · error
external processor unexpectedly sent response headers when r
Error message
external processor unexpectedly sent response headers when response header processing is disabled
What it means
Raised when the server sends a response_headers response while responseHeaderMode is modeSkip. The client never asked to process response headers, so a response-headers mutation is a protocol violation and the proc stream is failed.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1303
// 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
}
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
}View on GitHub (pinned to 03255a9237)
Solutions
- Set processing_mode.response_header_mode to SEND if you want header processing.
- Otherwise fix the server to not emit response_headers when the negotiated ProtocolConfiguration implies headers are skipped.
- Enable failure_mode_allow to bypass while fixing.
Example fix
// before processing_mode: response_header_mode: SKIP # server sends response_headers anyway // after processing_mode: response_header_mode: SEND
Defensive patterns
Strategy: validation
Validate before calling
// Server: suppress response_headers when the negotiated header mode is SKIP.
func maybeResponseHeaders(mode v3procfilterpb.ProcessingMode_HeaderSendMode, resp *pb.ProcessingResponse) *pb.ProcessingResponse {
if mode == v3procfilterpb.ProcessingMode_SKIP && resp.GetResponseHeaders() != nil {
return nil
}
return resp
} Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Set response_header_mode SEND iff the server will ack headers.
- Enable failure_mode_allow to tolerate mismatches.
When it happens
Trigger: processing_mode.response_header_mode == SKIP (or defaulted to SKIP via per-route override) and the server emits a ProcessingResponse.response_headers.
Common situations: Server always acks response headers regardless of mode. Per-route override disabled header processing for a route but the server is shared and assumes headers are on.
Related errors
- external processor unexpectedly sent request body when reque
- external processor unexpectedly sent response body when resp
- external processor sent response body before sending respons
- external processor sent response headers before response hea
- external processor unexpectedly sent duplicate response head
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/da7c11da4d21dea9.
Report an issue: GitHub.