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

  1. Ensure no middleware wraps c.Writer with a type lacking Flush on the LLMAutoSSE route
  2. Make custom ResponseWriter wrappers embed gin.ResponseWriter so Flush is inherited
  3. 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

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.