{"record":{"id":"b5c6cf6e58228f94","repo":"dapr/dapr","slug":"streaming-unsupported","errorCode":null,"errorMessage":"Streaming unsupported!","messagePattern":"Streaming unsupported!","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"pkg/sse/sse.go","lineNumber":77,"sourceCode":"\nfunc HandleSSEGrpcResponse(res *invokev1.InvokeMethodResponse) error {\n\tif res == nil {\n\t\treturn nil\n\t}\n\n\tstatusOK := res.Status().GetCode() >= 200 && res.Status().GetCode() < 300\n\tmsg := \"no response received from stream\"\n\tif statusOK {\n\t\tmsg = \"no expected response from stream\"\n\t}\n\n\treturn status.Errorf(codes.Internal, messages.ErrChannelInvoke, errors.New(msg))\n}\n\nfunc FlushSSEResponse(ctx context.Context, writer http.ResponseWriter, reader io.Reader) error {\n\tflusher, ok := writer.(http.Flusher)\n\tif !ok {\n\t\thttp.Error(writer, \"Streaming unsupported!\", http.StatusInternalServerError)\n\t\treturn nil\n\t}\n\n\t// Add defer close for streaming case\n\tcloser, ok := reader.(io.Closer)\n\tif ok {\n\t\tdefer closer.Close()\n\t}\n\n\t// Stream SSE data in real-time\n\tbuf := make([]byte, 1024)\n\tfor {\n\t\tif err := ctx.Err(); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tn, err := reader.Read(buf)\n\t\tif n > 0 {","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/dapr/dapr/blob/74ad41702745709bb15fe2114ff693b8c59bc3cc/pkg/sse/sse.go#L59-L95","documentation":"FlushSSEResponse streams server-sent events by asserting the http.ResponseWriter implements http.Flusher; when it doesn't (a middleware wrapped the writer without forwarding Flush), it writes 500 'Streaming unsupported!' and returns nil — the error is deliberately swallowed, so callers see success while the client got a 500.","triggerScenarios":"Invoking the SSE/grpc-proxy streaming path through a ResponseWriter wrapper (gzip, logging, metrics, custom auth middleware) whose concrete type lacks a Flush method; HTTP/1.0-style connections or test writers without flush support.","commonSituations":"Adding compression or observability middleware in front of handlers that wrap http.ResponseWriter and forget to delegate Flusher; unit tests using a custom recorder that does not implement Flush.","solutions":["Make wrapper types implement Flush() by delegating to the underlying writer when it is an http.Flusher","Move SSE routes outside the wrapping middleware chain","In tests, decorate recorders with a Flush method or use writers that implement http.Flusher"],"exampleFix":"// before\ntype logWriter struct{ http.ResponseWriter }\n// after\ntype logWriter struct {\n\thttp.ResponseWriter\n}\n\nfunc (l logWriter) Flush() {\n\tif f, ok := l.ResponseWriter.(http.Flusher); ok {\n\t\tf.Flush()\n\t}\n}","handlingStrategy":"type-guard","validationCode":"// Wire the SSE route only through writers that can flush.\nfunc supportsFlush(w http.ResponseWriter) (http.Flusher, bool) {\n\tf, ok := w.(http.Flusher)\n\treturn f, ok\n}","typeGuard":"func canStreamSSE(w http.ResponseWriter) bool {\n\t_, ok := w.(http.Flusher)\n\treturn ok\n}","tryCatchPattern":"if f, ok := w.(http.Flusher); ok {\n\tf.Flush()\n} else {\n\t// degrade: buffer the response instead of streaming, or 501 with a clear error\n\thttp.Error(w, \"streaming requires http.Flusher support\", http.StatusNotImplemented)\n}","preventionTips":["Give every ResponseWriter wrapper a delegating Flush() method","Assert Flusher support in middleware unit tests that wrap streaming handlers","Keep SSE routes outside compression/wrapping middleware chains"],"tags":["sse","streaming","http-flusher","middleware","grpc-proxy"],"backgroundTag":null,"analyzedSha":"74ad41702745709bb15fe2114ff693b8c59bc3cc","analyzedAt":"2026-08-16T04:22:26.543Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}