grpc/grpc-go · error
external processor sent response headers before response hea
Error message
external processor sent response headers before response headers were sent to it
What it means
Raised when the server sends response_headers before the client has sent response headers to it (responseHeaderSent is false). The ext-proc server must only mutate headers it has first received; responding with header mutations unsolicited is out of order and fails the proc stream.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1307
}
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
}
if err = cs.applyMutations(header.GetResponse().GetHeaderMutation(), cs.responseHeader); err != nil {
cs.failProcStream(err)
return
}View on GitHub (pinned to 03255a9237)
Solutions
- Make the server wait to receive response_headers from the client before sending any response_headers response.
- If the intent is request-side injection, do it in a request_headers response instead.
- Enable failure_mode_allow to bypass during remediation.
Example fix
// before: server pushes response headers unsolicited
func Process(stream) {
stream.Send(&pb.ProcessingResponse{ResponseHeaders: inject})
}
// after: wait for client to send response headers first
func Process(stream) {
for {
req, _ := stream.Recv()
if req.GetResponseHeaders() != nil {
stream.Send(&pb.ProcessingResponse{ResponseHeaders: mutate(req)})
}
}
} Defensive patterns
Strategy: validation
Validate before calling
// Server: only send response_headers after receiving response_headers from client.
seenRespHeaders := false
// for each req from Recv: if req.GetResponseHeaders()!=nil { seenRespHeaders=true }
// only emit response_headers resp when seenRespHeaders Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Never push response_headers unsolicited; always react to the client's message.
- For request-side injection, use a request_headers response.
When it happens
Trigger: The server emits a response_headers ProcessingResponse without first receiving a response_headers ProcessingRequest from the client.
Common situations: Server pushes response-header mutations proactively (e.g. to inject headers) instead of waiting for the client's response_headers message. Misuse of a server-side-initiated pattern that the protocol does not allow.
Related errors
- external processor sent response body before sending respons
- 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/4273cb6fc2c8cc26.
Report an issue: GitHub.