grpc/grpc-go · error
gRPC requires a ResponseWriter supporting http.Flusher
Error message
gRPC requires a ResponseWriter supporting http.Flusher
What it means
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).
Source
Thrown at internal/transport/handler_server.go:75
http.Error(w, msg, http.StatusMethodNotAllowed)
return nil, errors.New(msg)
}
contentType := r.Header.Get("Content-Type")
// TODO: do we assume contentType is lowercase? we did before
contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
if !validContentType {
msg := fmt.Sprintf("invalid gRPC request content-type %q", contentType)
http.Error(w, msg, http.StatusUnsupportedMediaType)
return nil, errors.New(msg)
}
if r.ProtoMajor != 2 {
msg := "gRPC requires HTTP/2"
http.Error(w, msg, http.StatusHTTPVersionNotSupported)
return nil, errors.New(msg)
}
if _, ok := w.(http.Flusher); !ok {
msg := "gRPC requires a ResponseWriter supporting http.Flusher"
http.Error(w, msg, http.StatusInternalServerError)
return nil, errors.New(msg)
}
var localAddr net.Addr
if la := r.Context().Value(http.LocalAddrContextKey); la != nil {
localAddr, _ = la.(net.Addr)
}
var authInfo credentials.AuthInfo
if r.TLS != nil {
authInfo = credentials.TLSInfo{State: *r.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}
}
p := peer.Peer{
Addr: strAddr(r.RemoteAddr),
LocalAddr: localAddr,
AuthInfo: authInfo,
}
st := &serverHandlerTransport{
rw: w,View on GitHub (pinned to 03255a9237)
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.
Example fix
// before: a buffering wrapper hides Flush()
type bufWriter struct{ http.ResponseWriter }
func (b *bufWriter) Write(p []byte) (int, error) { /* buffer */ ... }
// passing &bufWriter{w} to the gRPC handler -> error
// after: promote Flusher
type bufWriter struct{ http.ResponseWriter }
func (b *bufWriter) Flush() {
if f, ok := b.ResponseWriter.(http.Flusher); ok { f.Flush() }
} Defensive patterns
Strategy: type-guard
Validate before calling
// Verify Flusher support before dispatching to the gRPC handler
func handler(w http.ResponseWriter, r *http.Request) {
if _, ok := w.(http.Flusher); !ok {
http.Error(w, "streaming requires http.Flusher", http.StatusInternalServerError)
return
}
// ...delegate to gRPC...
} Type guard
func supportsFlush(w http.ResponseWriter) bool {
_, ok := w.(http.Flusher)
return ok
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- malformed grpc-timeout: %v
- malformed binary metadata %q in header %q: %v
- credentials: rawConn is dispatched out of gRPC
- invalid gRPC request method %q
- invalid gRPC request content-type %q
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/23e758ece59dd7ae.
Report an issue: GitHub.