{"record":{"id":"8235308050ca7265","repo":"grpc/grpc-go","slug":"external-processor-unexpectedly-sent-request-body","errorCode":null,"errorMessage":"external processor unexpectedly sent request body when request body processing is disabled","messagePattern":"external processor unexpectedly sent request body when request body processing is disabled","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extproc/ext_proc.go","lineNumber":1423,"sourceCode":"\t\tif err != nil {\n\t\t\tcs.failProcStream(err)\n\t\t\treturn\n\t\t}\n\t\tif resp.GetRequestDrain() {\n\t\t\t// Trigger the drain but continue receiving the drained messages until we\n\t\t\t// get io.EOF.\n\t\t\tcs.triggerBypass()\n\t\t}\n\n\t\tif resp.GetImmediateResponse() != nil {\n\t\t\tcs.handleImmediateResponse(resp.GetImmediateResponse(), newStream, opts)\n\t\t\treturn\n\t\t}\n\n\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","sourceCodeStart":1405,"sourceCodeEnd":1441,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/httpfilter/extproc/ext_proc.go#L1405-L1441","documentation":"Raised by recvFromProcServerLoop (ext_proc.go:1423) when the ext_proc server sends a request_body response but the configured requestBodyMode is modeSkip (NONE). Sending request body mutations the client never asked for is a protocol violation; failProcStream is called, which fails the RPC with codes.Internal unless failure_mode_allow bypasses it.","triggerScenarios":"Triggered when processing_mode.request_body_mode is NONE/SKIP yet the ext_proc server returns a ProcessingResponse with the request_body field set (ext_proc.go:1421).","commonSituations":"Server-side processing-mode mismatch (server assumes request body is enabled while the xDS config has NONE), shared/templated ext_proc handler that always echoes body mutations, or a control-plane config drift between what the server thinks and what the client advertised.","solutions":["Make the ext_proc server honor the ProtocolConfiguration.request_body_mode the client sent and only return request_body when it is GRPC.","Set request_body_mode to GRPC in the xDS config if body mutation is actually desired.","Enable failure_mode_allow so the client bypasses ext_proc instead of failing the user RPC.","Audit the server handler to ensure it does not emit request_body responses on the SKIP path."],"exampleFix":"// before: server always returns request body regardless of negotiated mode\nreturn &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_RequestBody{...}}, nil\n\n// after: only return when the client advertised request body mode GRPC\nif protocolCfg.GetRequestBodyMode() == procpb.BodySendMode_GRPC {\n  return &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_RequestBody{...}}, nil\n}\nreturn &procpb.ProcessingResponse{Response: &procpb.ProcessingResponse_RequestHeaders{...}}, nil","handlingStrategy":"fallback","validationCode":"// On the ext_proc SERVER: only emit request_body when the client negotiated GRPC.\nfunc shouldEmitRequestBody(protocolCfg *procpb.ProtocolConfiguration) bool {\n    return protocolCfg.GetRequestBodyMode() == procpb.BodySendMode_GRPC\n}","typeGuard":null,"tryCatchPattern":"// On the CLIENT: set failure_mode_allow so a server protocol violation bypasses\n// ext_proc instead of failing the user RPC.\nfilter.failure_mode_allow = true\n// Then optionally catch codes.Internal to surface/log ext_proc bypass.\nif st, ok := status.FromError(err); ok && st.Code() == codes.Internal &&\n    strings.Contains(st.Message(), \"unexpectedly sent request body\") {\n    // ext_proc server violated the negotiated mode; investigate server-side\n}","preventionTips":["Make the ext_proc server read ProtocolConfiguration.request_body_mode at stream open and skip request_body output when it is NONE.","Keep server and xDS processing_mode in sync (deploy them together).","Enable failure_mode_allow in production so a mode mismatch degrades rather than fails calls.","Add a server-side integration test that runs against a SKIP config and asserts no request_body is sent."],"tags":["grpc","xds","extproc","envoy","protocol-violation","request-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"}