flipped-aurora/gin-vue-admin · error
读取上游非流式响应失败: %w
Error message
读取上游非流式响应失败: %w
What it means
If the upstream response Content-Type is neither text/event-stream nor text/plain, the handler switches to blocking mode and reads the entire body with io.ReadAll. This error wraps a failure of that full-body read (connection reset, truncated response, timeout mid-read).
Source
Thrown at server/api/v1/system/sys_auto_code_sse.go:73
}
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode >= 300 {
body, readErr := io.ReadAll(res.Body)
if readErr != nil {
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 {View on GitHub (pinned to 3136500ef3)
Solutions
- Retry the request; transient connection resets are the usual cause
- Check upstream service logs/health for premature connection closure
- Verify no proxy/load-balancer between services is cutting long responses (timeouts, buffer limits)
- Consider setting a client timeout large enough for blocking mode responses
Example fix
// before
body, readErr := io.ReadAll(res.Body)
if readErr != nil { return fmt.Errorf("读取上游非流式响应失败: %w", readErr) }
// after
body, readErr := io.ReadAll(io.LimitReader(res.Body, maxBodySize))
if readErr != nil {
return fmt.Errorf("读取上游非流式响应失败: %w", readErr) // inspect wrapped net/http errors for reset/timeout
} Defensive patterns
Strategy: retry
Validate before calling
ct := res.Header.Get("Content-Type")
if !strings.Contains(ct, "text/event-stream") && !strings.Contains(ct, "text/plain") {
// expect blocking JSON; read with limit and timeout
} Try / catch
body, err := io.ReadAll(res.Body)
if err != nil {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() { /* retry with backoff */ }
return err
} Prevention
- Set http.Client timeouts longer than the longest blocking response
- Use io.LimitReader to bound body size
- Monitor for connection resets between proxy and upstream
When it happens
Trigger: Upstream returns 2xx with a non-streaming Content-Type (e.g. application/json in blocking mode) and io.ReadAll(res.Body) fails because the connection is closed or reset before the body completes.
Common situations: Upstream closes the connection mid-response; network interruption between proxy and LLM service; upstream returns JSON but dies before finishing the write.
Related errors
- 上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s
- 调用上游大模型流式服务失败: %w
- LLM request failed
- result.Msg
- 上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/0458f17619cf117d.
Report an issue: GitHub.