grpc/grpc-go · error
external processor sent response trailers before response tr
Error message
external processor sent response trailers before response trailers were sent to it
What it means
Raised when the server sends response_trailers before the client has sent response trailers to it (trailerSent is false). The server may only mutate trailers it has first received; an unsolicited response-trailers message is out of order and fails the proc stream.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1337
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
- Make the server wait for the client's response_trailers message before sending response_trailers.
- Track stream lifecycle in the server so trailers are only echoed/mutated once received.
- Enable failure_mode_allow to bypass while remediating.
Example fix
// before: server pushes trailers unsolicited
func Process(stream) {
stream.Send(&pb.ProcessingResponse{ResponseTrailers: inject})
}
// after: wait for client trailers
for {
req, _ := stream.Recv()
if req.GetResponseTrailers() != nil {
stream.Send(&pb.ProcessingResponse{ResponseTrailers: mutate(req)}); return
}
} Defensive patterns
Strategy: validation
Validate before calling
// Server: only send response_trailers after receiving the client's response_trailers.
seenRespTrailers := false
// for each req: if req.GetResponseTrailers()!=nil { seenRespTrailers=true }
// only emit response_trailers when seenRespTrailers Try / catch
// Client: failure_mode_allow -> bypass.
Prevention
- Always wait for the client's response_trailers before mutating trailers.
- Track end-of-stream carefully so trailers are exchanged once.
When it happens
Trigger: The server emits a response_trailers ProcessingResponse without first receiving the client's response_trailers ProcessingRequest.
Common situations: Server proactively injects trailers at end-of-stream instead of reacting to the client's trailer message. Server confuses request-body EndOfStream with the trailer exchange.
Related errors
- external processor sent response body after response trailer
- external processor sent response body before sending respons
- external processor sent response headers before response hea
- external processor unexpectedly sent duplicate response head
- external processor unexpectedly sent response trailers when
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/16d52958afc35463.
Report an issue: GitHub.