{"record":{"id":"c18eff3169098505","repo":"grpc/grpc-go","slug":"external-processor-unexpectedly-sent-response-body","errorCode":null,"errorMessage":"external processor unexpectedly sent response body when response body processing is disabled","messagePattern":"external processor unexpectedly sent response body when response body processing is disabled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extproc/ext_proc.go","lineNumber":1438,"sourceCode":"\t\tswitch {\n\t\tcase resp.GetRequestBody() != nil:\n\t\t\tif cs.config.processingModes.requestBodyMode == modeSkip {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor unexpectedly sent request body when request body processing is disabled\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tstreamedResp, ok := cs.validateBodyResponse(resp.GetRequestBody())\n\t\t\tif !ok {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif streamedResp.GetEndOfStream() {\n\t\t\t\tcs.discardRequests.Store(true)\n\t\t\t}\n\t\t\tcs.mutatedReqBuffer.Put(streamedResp)\n\n\t\tcase resp.GetResponseBody() != nil:\n\t\t\tif cs.config.processingModes.responseBodyMode == modeSkip {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor unexpectedly sent response body when response body processing is disabled\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If response headers have been sent and mutated response headers have\n\t\t\t// not been received before receiving the response body message, fail the\n\t\t\t// RPC.\n\t\t\tif cs.config.processingModes.responseHeaderMode == modeSend && !cs.responseHeadersReady.HasFired() {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor sent response body before sending response headers\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If mutated response trailers have been received before receiving the\n\t\t\t// response body message, fail the RPC.\n\t\t\tif cs.config.processingModes.responseTrailerMode == modeSend && cs.responseTrailerReady.HasFired() {\n\t\t\t\tcs.failProcStream(fmt.Errorf(\"external processor sent response body after response trailers were already processed\"))\n\t\t\t\treturn\n\t\t\t}\n","sourceCodeStart":1420,"sourceCodeEnd":1456,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/httpfilter/extproc/ext_proc.go#L1420-L1456","documentation":"Raised by recvFromProcServerLoop (ext_proc.go:1438) when the ext_proc server sends a response_body response but responseBodyMode is modeSkip (NONE). This is a protocol violation; failProcStream fails the RPC with codes.Internal unless failure_mode_allow bypasses ext_proc.","triggerScenarios":"Triggered when processing_mode.response_body_mode is NONE/SKIP but the server returns a ProcessingResponse with response_body set (ext_proc.go:1436).","commonSituations":"Server assumes response body processing is on while xDS configured NONE, a generic handler always mutating response bodies, or version drift between server expectations and the advertised ProtocolConfiguration.","solutions":["Ensure the server only returns response_body when ProtocolConfiguration.response_body_mode == GRPC.","Set response_body_mode to GRPC in the xDS config if you actually want response body mutation.","Enable failure_mode_allow so the dataplane RPC continues without ext_proc on this violation.","Fix any shared handler template that unconditionally emits response body mutations."],"exampleFix":"// before: server emits response body even though client mode is NONE\nreturn &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_ResponseBody{...}}, nil\n\n// after: gate response body output on the negotiated mode\nif protocolCfg.GetResponseBodyMode() == procpb.BodySendMode_GRPC {\n  return &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_ResponseBody{...}}, nil\n}","handlingStrategy":"fallback","validationCode":"// On the ext_proc SERVER: only emit response_body when the client negotiated GRPC.\nfunc shouldEmitResponseBody(protocolCfg *procpb.ProtocolConfiguration) bool {\n    return protocolCfg.GetResponseBodyMode() == procpb.BodySendMode_GRPC\n}","typeGuard":null,"tryCatchPattern":"filter.failure_mode_allow = true\nif st, ok := status.FromError(err); ok && st.Code() == codes.Internal &&\n    strings.Contains(st.Message(), \"unexpectedly sent response body\") {\n    // server violated the negotiated response body mode\n}","preventionTips":["Gate server response_body output on ProtocolConfiguration.response_body_mode == GRPC.","Avoid shared handler templates that always emit response body mutations.","Use failure_mode_allow to prevent the dataplane RPC from failing on a mode mismatch.","Test the server against a NONE response body config."],"tags":["grpc","xds","extproc","envoy","protocol-violation","response-body","processing-mode"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}