{"record":{"id":"9ad6cdf56c6a1681","repo":"grpc/grpc-go","slug":"external-processor-returned-unexpected-status-v-f","errorCode":null,"errorMessage":"external processor returned unexpected status %v for response headers, expected %v","messagePattern":"external processor returned unexpected status (.+?) for response headers, expected (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extproc/ext_proc.go","lineNumber":1319,"sourceCode":"\t\tcase resp.GetResponseHeaders() != nil:\n\t\t\tif cs.config.processingModes.responseHeaderMode == modeSkip {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor unexpectedly sent response headers when response header processing is disabled\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !cs.responseHeaderSent.Load() {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor sent response headers before response headers were sent to it\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif cs.responseHeadersReady.HasFired() {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor unexpectedly sent duplicate response headers after response headers were already processed\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\theader := resp.GetResponseHeaders()\n\t\t\t// Check if the status in the header response is CONTINUE; if not, fail\n\t\t\t// the stream.\n\t\t\tif status := header.GetResponse().GetStatus(); status != v3procservicepb.CommonResponse_CONTINUE {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor returned unexpected status %v for response headers, expected %v\", status, v3procservicepb.CommonResponse_CONTINUE))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err = cs.applyMutations(header.GetResponse().GetHeaderMutation(), cs.responseHeader); err != nil {\n\t\t\t\tcs.failProcStream(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Signal that the response header is modified and ready to be sent to the\n\t\t\t// client, so that if there is any buffered response body, it can be sent\n\t\t\t// after the header.\n\t\t\tcs.fireResponseHeadersReady()\n\n\t\tcase resp.GetResponseTrailers() != nil:\n\t\t\tif cs.config.processingModes.responseTrailerMode == modeSkip {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor unexpectedly sent response trailers when response trailer processing is disabled\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !cs.trailerSent.Load() {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor sent response trailers before response trailers were sent to it\"))","sourceCodeStart":1301,"sourceCodeEnd":1337,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/httpfilter/extproc/ext_proc.go#L1301-L1337","documentation":"Raised when the server's response_headers message carries a CommonResponse status other than CONTINUE. For response headers the gRPC extproc client only permits CONTINUE (apply mutations and continue the stream); any other status is rejected and the proc stream is failed.","triggerScenarios":"The server sets response_headers.response.status to something other than CommonResponse_CONTINUE (e.g. it tries to use the Envoy-style RESET_STREAM/ERROR or an immediate-response status on response headers).","commonSituations":"Server ported from Envoy ext_proc where header responses can carry other statuses. Server attempts to short-circuit the response via the header message instead of using the immediate_response field.","solutions":["Set response_headers.response.status = CONTINUE for every response-headers message.","If the server needs to abort/replace the response, use immediate_response instead of a non-CONTINUE header status.","Enable failure_mode_allow to bypass while fixing."],"exampleFix":"// before\nstream.Send(&pb.ProcessingResponse{ResponseHeaders:&pb.HeadersResponse{Response:&pb.CommonResponse{Status: pb.CommonResponse_RESET_STREAM, HeaderMutation: m}}})\n\n// after\nstream.Send(&pb.ProcessingResponse{ResponseHeaders:&pb.HeadersResponse{Response:&pb.CommonResponse{Status: pb.CommonResponse_CONTINUE, HeaderMutation: m}}})","handlingStrategy":"validation","validationCode":"// Server: force CONTINUE on response-headers responses.\nresp.Response.Status = pb.CommonResponse_CONTINUE","typeGuard":null,"tryCatchPattern":"// Client: failure_mode_allow -> bypass.","preventionTips":["Use immediate_response (not header status) to abort or replace the response.","Always set response_headers.response.status = CONTINUE."],"tags":["extproc","protocol-violation","response-headers","status","server-bug"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}