cloudflare/cloudflared · error

%T doesn't implement http.Flusher

Error message

%T doesn't implement http.Flusher

What it means

NewHTTP2RespWriter requires the http.ResponseWriter to also implement http.Flusher so it can flush streamed responses (SSE, websockets). If the writer does not, it builds an error `%T doesn't implement http.Flusher`, writes an error response on that writer, and returns nil, err. Callers (ServeHTTP/proxy helpers) then abort the request.

Source

Thrown at connection/http2.go:214

	w             http.ResponseWriter
	flusher       http.Flusher
	shouldFlush   bool
	statusWritten bool
	respHeaders   http.Header
	hijackedMutex sync.Mutex
	hijackedv     bool
	log           *zerolog.Logger
}

func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, log *zerolog.Logger) (*http2RespWriter, error) {
	flusher, isFlusher := w.(http.Flusher)
	if !isFlusher {
		respWriter := &http2RespWriter{
			r:   r.Body,
			w:   w,
			log: log,
		}
		err := fmt.Errorf("%T doesn't implement http.Flusher", w)
		respWriter.WriteErrorResponse(err)
		return nil, err
	}

	return &http2RespWriter{
		r:           r.Body,
		w:           w,
		flusher:     flusher,
		shouldFlush: connType.shouldFlush(),
		respHeaders: make(http.Header),
		log:         log,
	}, nil
}

func (rp *http2RespWriter) AddTrailer(trailerName, trailerValue string) {
	if !rp.statusWritten {
		rp.log.Warn().Msg("Tried to add Trailer to response before status written. Ignoring...")
		return

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. If using custom middleware, make the wrapper implement http.Flusher by delegating Flush() to the inner writer
  2. In tests, use a writer that implements http.Flusher (httptest.ResponseRecorder does not, wrap it)
  3. Ensure the middleware passes http.ResponseWriter through unmodified when possible
  4. Upgrade cloudflared if this appears in stock proxying paths

Example fix

// before
type noFlushWriter struct{ http.ResponseWriter }
// after
type flushingWriter struct{ http.ResponseWriter }
func (w *flushingWriter) Flush() { w.ResponseWriter.(http.Flusher).Flush() }
Defensive patterns

Strategy: type-guard

Type guard

func asFlusher(w http.ResponseWriter) (http.Flusher, bool) {
    f, ok := w.(http.Flusher)
    return f, ok
}

Try / catch

if _, ok := w.(http.Flusher); !ok {
    // don't hand w to code needing streaming; use a buffered non-streaming path instead
}

Prevention

When it happens

Trigger: NewHTTP2RespWriter is called with an http.ResponseWriter whose concrete type lacks a Flush() method — i.e. the edge/http2 stack supplied a non-flushing writer, or a test/proxy wrapper implements only http.ResponseWriter.

Common situations: Custom middleware wrapping the ResponseWriter without forwarding Flush(), test harnesses using httptest writers without Flusher support, or an http2 transport regression in the library itself.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/9419953515bbd6bd. Report an issue: GitHub.