{"record":{"id":"23e758ece59dd7ae","repo":"grpc/grpc-go","slug":"grpc-requires-a-responsewriter-supporting-http-flu","errorCode":null,"errorMessage":"gRPC requires a ResponseWriter supporting http.Flusher","messagePattern":"gRPC requires a ResponseWriter supporting http\\.Flusher","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"internal/transport/handler_server.go","lineNumber":75,"sourceCode":"\t\thttp.Error(w, msg, http.StatusMethodNotAllowed)\n\t\treturn nil, errors.New(msg)\n\t}\n\tcontentType := r.Header.Get(\"Content-Type\")\n\t// TODO: do we assume contentType is lowercase? we did before\n\tcontentSubtype, validContentType := grpcutil.ContentSubtype(contentType)\n\tif !validContentType {\n\t\tmsg := fmt.Sprintf(\"invalid gRPC request content-type %q\", contentType)\n\t\thttp.Error(w, msg, http.StatusUnsupportedMediaType)\n\t\treturn nil, errors.New(msg)\n\t}\n\tif r.ProtoMajor != 2 {\n\t\tmsg := \"gRPC requires HTTP/2\"\n\t\thttp.Error(w, msg, http.StatusHTTPVersionNotSupported)\n\t\treturn nil, errors.New(msg)\n\t}\n\tif _, ok := w.(http.Flusher); !ok {\n\t\tmsg := \"gRPC requires a ResponseWriter supporting http.Flusher\"\n\t\thttp.Error(w, msg, http.StatusInternalServerError)\n\t\treturn nil, errors.New(msg)\n\t}\n\n\tvar localAddr net.Addr\n\tif la := r.Context().Value(http.LocalAddrContextKey); la != nil {\n\t\tlocalAddr, _ = la.(net.Addr)\n\t}\n\tvar authInfo credentials.AuthInfo\n\tif r.TLS != nil {\n\t\tauthInfo = credentials.TLSInfo{State: *r.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}\n\t}\n\tp := peer.Peer{\n\t\tAddr:      strAddr(r.RemoteAddr),\n\t\tLocalAddr: localAddr,\n\t\tAuthInfo:  authInfo,\n\t}\n\tst := &serverHandlerTransport{\n\t\trw:             w,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/transport/handler_server.go#L57-L93","documentation":"NewServerHandlerTransport (handler_server.go:53) lets you run a gRPC server inside a standard net/http.Handler. It requires the http.ResponseWriter to implement http.Flusher because gRPC streaming relies on flushing frames to the client as they are produced. If w.(http.Flusher) fails, it writes an HTTP 500 and returns this error (it is a returned error, not a panic).","triggerScenarios":"Calling NewServerHandlerTransport with a ResponseWriter that does not satisfy http.Flusher. The standard net/http server's ResponseWriter DOES implement Flusher (when Flush is available), but a custom/ wrapped ResponseWriter (e.g. a buffering middleware, a gzip writer, a test httptest mock missing Flusher) may not.","commonSituations":"Wrapping the http.ResponseWriter in a buffering or compressing middleware before delegating to the gRPC handler; an httptest.ResponseRecorder (which does implement Flusher, but a custom test double may not); running behind a proxy that decorates the writer.","solutions":["Ensure the ResponseWriter passed to the gRPC http handler is the original or a wrapper that embeds/promotes http.Flusher.","If you wrap the writer, implement Flush() on your wrapper that delegates to the underlying Flusher.","Do not insert a buffering layer between the HTTP server and the gRPC handler."],"exampleFix":"// before: a buffering wrapper hides Flush()\ntype bufWriter struct{ http.ResponseWriter }\nfunc (b *bufWriter) Write(p []byte) (int, error) { /* buffer */ ... }\n// passing &bufWriter{w} to the gRPC handler -> error\n\n// after: promote Flusher\ntype bufWriter struct{ http.ResponseWriter }\nfunc (b *bufWriter) Flush() {\n    if f, ok := b.ResponseWriter.(http.Flusher); ok { f.Flush() }\n}","handlingStrategy":"type-guard","validationCode":"// Verify Flusher support before dispatching to the gRPC handler\nfunc handler(w http.ResponseWriter, r *http.Request) {\n    if _, ok := w.(http.Flusher); !ok {\n        http.Error(w, \"streaming requires http.Flusher\", http.StatusInternalServerError)\n        return\n    }\n    // ...delegate to gRPC...\n}","typeGuard":"func supportsFlush(w http.ResponseWriter) bool {\n    _, ok := w.(http.Flusher)\n    return ok\n}","tryCatchPattern":null,"preventionTips":["Do not wrap the ResponseWriter in a buffering/compressing layer that hides http.Flusher.","If you must wrap, promote Flush() to delegate to the underlying Flusher.","In tests, use httptest.NewServer (its writer supports Flusher) rather than custom mocks."],"tags":["transport","http","handler","grpc-go","streaming"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}