flipped-aurora/gin-vue-admin · error
当前响应不支持流式输出
Error message
当前响应不支持流式输出
What it means
streamLLMAsSSE converts a non-stream upstream LLM response into a single SSE event. Before writing anything it asserts the gin ResponseWriter implements http.Flusher; if not, it returns '当前响应不支持流式输出' and no headers/status are sent.
Source
Thrown at server/api/v1/system/sys_auto_code_sse.go:79
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w", res.StatusCode, res.Header.Get("Content-Type"), readErr)
}
return fmt.Errorf("上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s", res.StatusCode, res.Header.Get("Content-Type"), previewResponseBody(body))
}
ct := res.Header.Get("Content-Type")
logger.WithCtx(c.Request.Context()).Mod("biz").Field("status", res.StatusCode).Field("content-type", ct).Info("LLMAutoSSE 上游返回成功,开始 SSE 流式转发")
// 如果上游返回的不是 SSE 流(可能是 blocking 模式返回的 JSON),直接读取并转发
if !strings.Contains(ct, "text/event-stream") && !strings.Contains(ct, "text/plain") {
body, readErr := io.ReadAll(res.Body)
if readErr != nil {
return fmt.Errorf("读取上游非流式响应失败: %w", readErr)
}
logger.WithCtx(c.Request.Context()).Mod("biz").Field("body_preview", previewResponseBody(body)).Warn("LLMAutoSSE 上游返回非 SSE 流,Content-Type: "+ct+", 将以单次事件转发")
flusher, ok := c.Writer.(http.Flusher)
if !ok {
return errors.New("当前响应不支持流式输出")
}
prepareSSEHeaders(c)
c.Status(http.StatusOK)
var payload any
if err := json.Unmarshal(body, &payload); err != nil {
payload = string(body)
}
if err := renderSSE(c, sse.Event{Event: "message", Data: payload}); err != nil {
return err
}
if err := renderSSE(c, sse.Event{Event: "done", Data: gin.H{"done": true}}); err != nil {
return err
}
flusher.Flush()
return nil
}
View on GitHub (pinned to 3136500ef3)
Solutions
- Ensure no middleware wraps c.Writer with a type lacking Flush on the LLMAutoSSE route
- Make custom ResponseWriter wrappers embed gin.ResponseWriter so Flush is inherited
- Run the route against the plain gin engine to isolate the wrapper causing it
Defensive patterns
Strategy: validation
Validate before calling
func assertFlushable(c *gin.Context) error {
if _, ok := c.Writer.(http.Flusher); !ok {
return errors.New("当前响应不支持流式输出: writer is " + fmt.Sprintf("%T", c.Writer))
}
return nil
}
// call at the top of any SSE handler Try / catch
if err := streamLLMAsSSE(c, upstream); err != nil {
if !c.Writer.Written() {
c.JSON(http.StatusInternalServerError, gin.H{"code": 7, "msg": err.Error()})
}
} Prevention
- Run an integration test that streams LLMAutoSSE and asserts chunked flushing
- Avoid global middleware that swaps Context.Writer (logging/recovery wrappers included)
- Include the writer's concrete type in the error for faster diagnosis
When it happens
Trigger: Upstream returned a non-SSE (buffered) response, the code reads it fully, then c.Writer.(http.Flusher) fails when preparing the single-event SSE reply.
Common situations: Middleware replacing Context.Writer with a non-Flushing wrapper; embedding gin handlers inside another framework (e.g. wrapped by prometheus/recovery writers); test recorders without Flush support.
Related errors
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/b96f44100884cf33.
Report an issue: GitHub.