{"record":{"id":"56471f72e965865d","repo":"wavetermdev/waveterm","slug":"streaming-not-supported","errorCode":null,"errorMessage":"streaming not supported","messagePattern":"streaming not supported","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"tsunami/engine/serverhandlers.go","lineNumber":492,"sourceCode":"\n\t// Generate unique connection ID for this SSE connection\n\tconnectionId := fmt.Sprintf(\"%s-%d\", clientId, time.Now().UnixNano())\n\n\t// Register SSE channel for this connection\n\teventCh := h.Client.RegisterSSEChannel(connectionId)\n\tdefer h.Client.UnregisterSSEChannel(connectionId)\n\n\t// Set SSE headers\n\tsetNoCacheHeaders(w)\n\tw.Header().Set(\"Content-Type\", \"text/event-stream\")\n\tw.Header().Set(\"Connection\", \"keep-alive\")\n\tw.Header().Set(\"X-Accel-Buffering\", \"no\")\n\tw.Header().Set(\"X-Content-Type-Options\", \"nosniff\")\n\n\t// Use ResponseController for better flushing control\n\trc := http.NewResponseController(w)\n\tif err := rc.Flush(); err != nil {\n\t\thttp.Error(w, \"streaming not supported\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\t// Create a ticker for keepalive packets\n\tkeepaliveTicker := time.NewTicker(SSEKeepAliveDuration)\n\tdefer keepaliveTicker.Stop()\n\n\tfor {\n\t\tselect {\n\t\tcase <-r.Context().Done():\n\t\t\treturn\n\t\tcase <-keepaliveTicker.C:\n\t\t\t// Send keepalive comment\n\t\t\tfmt.Fprintf(w, \": keepalive\\n\\n\")\n\t\t\trc.Flush()\n\t\tcase event := <-eventCh:\n\t\t\tif event.Event == \"\" {\n\t\t\t\tbreak","sourceCodeStart":474,"sourceCodeEnd":510,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/tsunami/engine/serverhandlers.go#L474-L510","documentation":"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.","triggerScenarios":"Request path wrapped in middleware that buffers or replaces the ResponseWriter with a non-flushing implementation (custom wrappers, test recorders, some compressing/buffering middleware).","commonSituations":"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).","solutions":["Remove or fix middleware that wraps ResponseWriter without implementing http.Flusher (embed Flusher and delegate)","Ensure SSE routes bypass compression/buffering middleware","In tests, use an httptest server with a real ResponseWriter instead of ResponseRecorder","Verify the route is registered directly on the mux, not behind a buffering wrapper"],"exampleFix":"// before\ntype myRW struct { http.ResponseWriter } // no Flush\n// after\ntype myRW struct { http.ResponseWriter }\nfunc (m *myRW) Flush() { m.ResponseWriter.(http.Flusher).Flush() }","handlingStrategy":"fallback","validationCode":"// check Flusher support early\nif _, ok := w.(http.Flusher); !ok { log.Println('writer does not support flushing') }","typeGuard":null,"tryCatchPattern":"// middleware wrapper should forward Flush\nif fw, ok := w.(http.Flusher); ok { fw.Flush() } else { log.Println('flushing unavailable; SSE disabled, falling back to polling') }","preventionTips":["Any custom ResponseWriter wrapper must embed http.Flusher and delegate Flush","Bypass compression/buffering middleware for SSE routes","In tests use httptest.NewServer, not ResponseRecorder, for SSE","Add a startup check that the SSE route's writer chain supports Flush"],"tags":["sse","streaming","flusher"],"backgroundTag":"streaming-not-supported","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}