{"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":76,"sourceCode":"\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,\n\t\treq:            r,","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/transport/handler_server.go#L58-L94","documentation":"Returned by NewServerHandlerTransport when the http.ResponseWriter does not implement the http.Flusher interface. gRPC streaming requires flushing partial responses to the client as they are produced (for real-time streaming), so the ResponseWriter must support Flusher. The transport checks this with a type assertion at handler_server.go:73 and rejects with HTTP 500 if it fails.","triggerScenarios":"Wrapping or replacing the http.ResponseWriter passed to the gRPC handler with a custom type that does not embed or implement http.Flusher. Common with custom middleware, logging wrappers, or response recorders (e.g., httptest.ResponseRecorder in older Go versions did not implement Flusher, though modern ones do).","commonSituations":"Custom HTTP middleware wrapping the ResponseWriter for logging/metrics without forwarding Flush(); using httptest.ResponseRecorder that lacks Flusher (Go < 1.8); a response-buffering proxy that wraps the writer; test doubles that don't implement the full interface.","solutions":["Ensure any custom ResponseWriter wrapper embeds or delegates Flush() to the underlying writer.","Implement http.Flusher on your wrapper: func (w *myWriter) Flush() { w.w.(http.Flusher).Flush() }.","Check that middleware (logging, gzip, etc.) preserves the Flusher interface.","In tests, use real http.Server or ensure your ResponseRecorder implements Flusher."],"exampleFix":"// before: wrapper drops Flusher\ntype logWriter struct { http.ResponseWriter }\nfunc (lw *logWriter) Write(b []byte) (int, error) { return lw.ResponseWriter.Write(b) }\n\n// after: forward Flush()\ntype logWriter struct { http.ResponseWriter }\nfunc (lw *logWriter) Write(b []byte) (int, error) { return lw.ResponseWriter.Write(b) }\nfunc (lw *logWriter) Flush() {\n    if f, ok := lw.ResponseWriter.(http.Flusher); ok { f.Flush() }\n}","handlingStrategy":"type-guard","validationCode":"// Verify the ResponseWriter implements http.Flusher before passing to gRPC.\nif _, ok := w.(http.Flusher); !ok {\n    log.Fatal(\"ResponseWriter must support http.Flusher for gRPC streaming\")\n}","typeGuard":"// Type guard to ensure a ResponseWriter supports Flusher.\nfunc supportsFlusher(w http.ResponseWriter) bool {\n    _, ok := w.(http.Flusher)\n    return ok\n}","tryCatchPattern":"st, err := transport.NewServerHandlerTransport(w, r, stats, pool)\nif err != nil {\n    if strings.Contains(err.Error(), \"http.Flusher\") {\n        log.Fatal(\"wrap ResponseWriter with Flush() support for gRPC\")\n    }\n}","preventionTips":["Ensure any custom ResponseWriter wrapper delegates Flush() to the underlying writer.","Test middleware wrappers to confirm they implement http.Flusher.","Avoid response-buffering middleware in front of gRPC streaming endpoints.","Use standard net/http server which provides Flusher-capable writers."],"tags":["transport","http","handler-server","streaming","grpc","validation"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}