wavetermdev/waveterm · critical

streaming not supported

Error message

streaming not supported

What it means

After writing SSE headers, the handler calls http.NewResponseController(w).Flush() to verify the ResponseWriter supports streaming. If the writer does not implement Flusher (rc.Flush errors), it returns 500 'streaming not supported' because SSE cannot work.

Source

Thrown at tsunami/engine/serverhandlers.go:492

	// Generate unique connection ID for this SSE connection
	connectionId := fmt.Sprintf("%s-%d", clientId, time.Now().UnixNano())

	// Register SSE channel for this connection
	eventCh := h.Client.RegisterSSEChannel(connectionId)
	defer h.Client.UnregisterSSEChannel(connectionId)

	// Set SSE headers
	setNoCacheHeaders(w)
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Connection", "keep-alive")
	w.Header().Set("X-Accel-Buffering", "no")
	w.Header().Set("X-Content-Type-Options", "nosniff")

	// Use ResponseController for better flushing control
	rc := http.NewResponseController(w)
	if err := rc.Flush(); err != nil {
		http.Error(w, "streaming not supported", http.StatusInternalServerError)
		return
	}

	// Create a ticker for keepalive packets
	keepaliveTicker := time.NewTicker(SSEKeepAliveDuration)
	defer keepaliveTicker.Stop()

	for {
		select {
		case <-r.Context().Done():
			return
		case <-keepaliveTicker.C:
			// Send keepalive comment
			fmt.Fprintf(w, ": keepalive\n\n")
			rc.Flush()
		case event := <-eventCh:
			if event.Event == "" {
				break

View on GitHub (pinned to a4447c1563)

Solutions

  1. Remove or fix middleware that wraps ResponseWriter without implementing http.Flusher (embed Flusher and delegate)
  2. Ensure SSE routes bypass compression/buffering middleware
  3. In tests, use an httptest server with a real ResponseWriter instead of ResponseRecorder
  4. Verify the route is registered directly on the mux, not behind a buffering wrapper

Example fix

// before
type myRW struct { http.ResponseWriter } // no Flush
// after
type myRW struct { http.ResponseWriter }
func (m *myRW) Flush() { m.ResponseWriter.(http.Flusher).Flush() }
Defensive patterns

Strategy: fallback

Validate before calling

// check Flusher support early
if _, ok := w.(http.Flusher); !ok { log.Println('writer does not support flushing') }

Try / catch

// middleware wrapper should forward Flush
if fw, ok := w.(http.Flusher); ok { fw.Flush() } else { log.Println('flushing unavailable; SSE disabled, falling back to polling') }

Prevention

When it happens

Trigger: Request path wrapped in middleware that buffers or replaces the ResponseWriter with a non-flushing implementation (custom wrappers, test recorders, some compressing/buffering middleware).

Common situations: Running behind middleware that wraps ResponseWriter for logging/compression without forwarding Flush(); httptest.ResponseRecorder in tests; proxies configured to buffer responses (though those affect flushing, not this specific error).

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/56471f72e965865d. Report an issue: GitHub.