flipped-aurora/gin-vue-admin · error
上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w
Error message
上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w
What it means
The SSE variant of the non-2xx check: streamLLMAsSSE (called by LLMAutoSSE) inspects res.StatusCode after LLMAutoStream returns, and when reading the error body of a non-2xx response itself fails, returns the same '上游大模型流式服务返回非 2xx' message carrying status, Content-Type, and the body-read error via %w. It exists so SSE clients still receive a structured upstream failure even when the error payload is unreadable.
Source
Thrown at server/api/v1/system/sys_auto_code_sse.go:61
if c.Writer.Written() {
writeLLMStreamError(c, err)
return
}
response.FailWithMessage(err.Error(), c)
}
}
func (autoApi *AutoCodeApi) streamLLMAsSSE(c *gin.Context, llm common.JSONMap) error {
res, err := autoCodeService.LLMAutoStream(c.Request.Context(), llm)
if err != nil {
return fmt.Errorf("调用上游大模型失败: %w", err)
}
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("当前响应不支持流式输出")View on GitHub (pinned to 3136500ef3)
Solutions
- Inspect the wrapped read-body-err; 'unexpected EOF'/'connection reset' means the upstream closed the error response early.
- Verify API key, quota, and model configuration — non-2xx under SSE almost always traces to auth or rate limiting.
- Retry with backoff for transient 429/5xx; surface a server-sent error event to the SSE client so the UI can react.
- Capture upstream logs/status page to confirm whether the provider is dropping error responses during an incident.
Example fix
// before
// error swallowed as read failure, no context to debug
return fmt.Errorf("...read-body-err=%w", status, ct, readErr)
// after
logger.WithCtx(c.Request.Context()).Mod("biz").Field("status", status).Warn("上游返回非2xx且body不可读")
return fmt.Errorf("upstream non-2xx: status=%d (error body unreadable: %v)", status, readErr) Defensive patterns
Strategy: fallback
Validate before calling
resp, err := svc.LLMAutoStream(ctx, llm)
if err != nil { return err }
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("LLM upstream %d", resp.StatusCode) // decide SSE error event now
} Try / catch
if err := api.streamLLMAsSSE(c, payload); err != nil {
c.SSEvent("error", gin.H{"msg": "upstream LLM rejected the request"})
c.Writer.Flush()
return nil // keep the SSE channel valid for the client
} Prevention
- Send SSE 'error' events instead of aborting so clients get structured failures
- Pre-check API key/quota before opening streams
- Treat unreadable error bodies as auth/quota signals and alert on them
- Cap error-body reads with io.LimitReader and report status even when the body fails
When it happens
Trigger: LLMAutoSSE -> streamLLMAsSSE receives a 4xx/5xx from the upstream LLM whose body cannot be read: the upstream closed the chunked connection on error, the body was aborted mid-transfer, or the connection reset before any error bytes arrived.
Common situations: Expired API key causing the provider to reset the error response, upstream CDN/WAF closing connections on rejection, quota-exceeded 429 with aborted body, provider outage producing truncated 5xx responses.
Related errors
- 上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w
- 上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s
- 调用上游大模型失败: %w
- 调用上游大模型流式服务失败: %w
- LLM request failed
AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31).
Data as JSON: /api/errors/c24299ce3197888f.
Report an issue: GitHub.