{"record":{"id":"1a7d18e01cc5fbe8","repo":"grpc/grpc-go","slug":"external-processor-returned-unexpected-status-v-f-1a7d18","errorCode":null,"errorMessage":"external processor returned unexpected status %v for request headers, expected %v","messagePattern":"external processor returned unexpected status (.+?) for request headers, expected (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extproc/ext_proc.go","lineNumber":1549,"sourceCode":"\t\treturn false\n\t}\n\tif resp.GetRequestDrain() {\n\t\tcs.triggerBypass()\n\t}\n\tif resp.GetImmediateResponse() != nil {\n\t\tcs.handleImmediateResponse(resp.GetImmediateResponse(), newStream, opts)\n\t\treturn false\n\t}\n\n\theader := resp.GetRequestHeaders()\n\tif header == nil {\n\t\terr := fmt.Errorf(\"external processor returned an unexpected message type %T, expected request headers response\", resp.GetResponse())\n\t\tcs.handleHeaderError(err, newStream, opts)\n\t\treturn false\n\t}\n\t// Check if status in header response is CONTINUE; if not, fail the stream.\n\tif status := header.GetResponse().GetStatus(); status != v3procservicepb.CommonResponse_CONTINUE {\n\t\tcs.handleHeaderError(fmt.Errorf(\"external processor returned unexpected status %v for request headers, expected %v\", status, v3procservicepb.CommonResponse_CONTINUE), newStream, opts)\n\t\treturn false\n\t}\n\t// Mutate the outgoing headers with additions and removals received from the\n\t// external processor.\n\toutgoingMD, _ := metadata.FromOutgoingContext(ctx)\n\tif outgoingMD == nil {\n\t\toutgoingMD = metadata.MD{}\n\t}\n\tif err = cs.applyMutations(header.GetResponse().GetHeaderMutation(), outgoingMD); err != nil {\n\t\tcs.handleHeaderError(err, newStream, opts)\n\t\treturn false\n\t}\n\tdataplaneCtx := metadata.NewOutgoingContext(ctx, outgoingMD)\n\tif err = cs.createDataplaneStream(dataplaneCtx, newStream, opts); err != nil {\n\t\treturn false\n\t}\n\treturn true\n}","sourceCodeStart":1531,"sourceCodeEnd":1567,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/httpfilter/extproc/ext_proc.go#L1531-L1567","documentation":"After the request-headers handshake message is received, the filter verifies its CommonResponse status is CONTINUE (ext_proc.go:1548). A non-CONTINUE status is treated as an error during header processing and, depending on failure_mode_allow, either bypasses the processor or fails the RPC.","triggerScenarios":"The extproc server returns a request_headers response whose status is not CONTINUE (unset, or some other enum value).","commonSituations":"Server constructs the headers response without setting Status; server attempts to signal an error through status instead of using ImmediateResponse; protobuf default-value confusion (zero status is not CONTINUE).","solutions":["Set response.status = CONTINUE on the request_headers response from the extproc server.","If the server wants to reject, send an ImmediateResponse instead of a non-CONTINUE header status.","Set failure_mode_allow=true in config so header errors bypass cleanly while the server is fixed."],"exampleFix":"// before\nhdrResp.Response.Status = procservicepb.CommonResponse(0) // unset != CONTINUE\n\n// after\nhdrResp.Response.Status = procservicepb.CommonResponse_CONTINUE","handlingStrategy":"fallback","validationCode":"// Server side\nif hdrResp.GetResponse().GetStatus() != procservicepb.CommonResponse_CONTINUE {\n    hdrResp.Response.Status = procservicepb.CommonResponse_CONTINUE\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always set Status=CONTINUE on the request_headers response.","Use ImmediateResponse to reject rather than a non-CONTINUE status.","Set failure_mode_allow during development."],"tags":["extproc","grpc","xds","protocol","go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}