grpc/grpc-go · error
external processor returned an unexpected message type %T, e
Error message
external processor returned an unexpected message type %T, expected request headers response
What it means
The very first ProcessingResponse the extproc filter expects from the server after sending request headers must be a request_headers response (ext_proc.go:1541). If the response's oneof resolves to anything else (response headers, body, trailers, etc.), the filter cannot proceed and either bypasses (per failure mode) or fails.
Source
Thrown at internal/xds/httpfilter/extproc/ext_proc.go:1543
// returns false if the proc stream needs to be bypassed, or failed due to an
// error, or aborted early by a processor-initiated immediate response.
func (cs *clientStream) processInitialHeaders(ctx context.Context, newStream func(context.Context, ...grpc.CallOption) (grpc.ClientStream, error), opts []grpc.CallOption) bool {
resp, err := cs.procStream.Recv()
if err != nil {
cs.handleHeaderError(err, newStream, opts)
return false
}
if resp.GetRequestDrain() {
cs.triggerBypass()
}
if resp.GetImmediateResponse() != nil {
cs.handleImmediateResponse(resp.GetImmediateResponse(), newStream, opts)
return false
}
header := resp.GetRequestHeaders()
if header == nil {
err := fmt.Errorf("external processor returned an unexpected message type %T, expected request headers response", resp.GetResponse())
cs.handleHeaderError(err, newStream, opts)
return false
}
// Check if status in header response is CONTINUE; if not, fail the stream.
if status := header.GetResponse().GetStatus(); status != v3procservicepb.CommonResponse_CONTINUE {
cs.handleHeaderError(fmt.Errorf("external processor returned unexpected status %v for request headers, expected %v", status, v3procservicepb.CommonResponse_CONTINUE), newStream, opts)
return false
}
// Mutate the outgoing headers with additions and removals received from the
// external processor.
outgoingMD, _ := metadata.FromOutgoingContext(ctx)
if outgoingMD == nil {
outgoingMD = metadata.MD{}
}
if err = cs.applyMutations(header.GetResponse().GetHeaderMutation(), outgoingMD); err != nil {
cs.handleHeaderError(err, newStream, opts)
return false
}View on GitHub (pinned to 03255a9237)
Solutions
- Make the extproc server reply to the initial request_headers request with a ProcessingResponse whose request_headers field is set.
- Do not send body/response/trailer messages until the request-headers handshake completes.
- Re-test against the envoy.service.ext_proc.v3 contract; ensure the request/response pairing matches the processing modes.
Example fix
// before: server answers request headers with a response_headers message
stream.Send(&procservicepb.ProcessingResponse{Response: &procservicepb.ProcessingResponse_ResponseHeaders{ResponseHeaders: hv}})
// after: first reply must carry request_headers
stream.Send(&procservicepb.ProcessingResponse{Response: &procservicepb.ProcessingResponse_RequestHeaders{RequestHeaders: &procservicepb.HeadersResponse{Response: &procservicepb.CommonResponse{Status: procservicepb.CommonResponse_CONTINUE}}}}) Defensive patterns
Strategy: validation
Validate before calling
// Server side: guarantee the first message is a request_headers response
if !requestHeadersSent {
stream.Send(&procservicepb.ProcessingResponse{Response: &procservicepb.ProcessingResponse_RequestHeaders{RequestHeaders: hdrResp}})
requestHeadersSent = true
} Prevention
- Implement the extproc handshake ordering exactly: request_headers first, then body/trailer messages.
- Add a server-side state machine test for message ordering.
- Set failure_mode_allow to bypass cleanly while debugging.
When it happens
Trigger: The external processor's first message on the stream carries a non-request-headers response variant, e.g. it sent response_headers or a body response before acknowledging request headers.
Common situations: Server mis-implements the handshake ordering; server is a generic proxy that responds to the first request_headers_request with the wrong message type; state machine bug where the server replies with the final response type prematurely.
Related errors
- external processor unexpectedly sent duplicate response trai
- external processor returned unexpected status %v for body re
- external processor returned invalid body mutation in body re
- external processor returned compressed grpc message which is
- external processor returned unexpected status %v for request
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/ef6ae1870d026b90.
Report an issue: GitHub.