flipped-aurora/gin-vue-admin · error

读取上游流式响应失败: %w

Error message

读取上游流式响应失败: %w

What it means

After writing each chunk, proxyLLMStream checks the Read error from the upstream body. If it is not io.EOF (i.e. the stream ended abnormally rather than cleanly), it returns fmt.Errorf("读取上游流式响应失败: %w", readErr) — 'failed to read upstream streaming response' — wrapping the underlying read error so the client-facing caller of LLMAuto knows the upstream stream broke before completion.

Source

Thrown at server/api/v1/system/sys_auto_code.go:209

	c.Writer.Header().Set("Connection", "keep-alive")
	c.Writer.Header().Set("X-Accel-Buffering", "no")
	c.Status(res.StatusCode)
	flusher.Flush()

	buf := make([]byte, 32*1024)
	for {
		n, readErr := res.Body.Read(buf)
		if n > 0 {
			if _, writeErr := c.Writer.Write(buf[:n]); writeErr != nil {
				return fmt.Errorf("向客户端写入流式响应失败: %w", writeErr)
			}
			flusher.Flush()
		}
		if readErr != nil {
			if errors.Is(readErr, io.EOF) {
				return nil
			}
			return fmt.Errorf("读取上游流式响应失败: %w", readErr)
		}
	}
}

func copyLLMStreamHeaders(dst, src http.Header) {
	for _, key := range []string{
		"Content-Type",
		"Cache-Control",
		"Content-Encoding",
		"Content-Language",
		"X-Accel-Buffering",
	} {
		if value := src.Get(key); value != "" {
			dst.Set(key, value)
		}
	}
}

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Inspect the wrapped error: 'connection reset by peer'/'unexpected EOF' indicates upstream/network breakage; 'context deadline exceeded' indicates a timeout.
  2. Increase the outbound HTTP client's timeout / configure its Transport (idle conn, response header timeout) for long streams.
  3. Retry the generation once on transient read failures; make the client resume or regenerate gracefully on truncated answers.
  4. Check provider status pages/logs — repeated mid-stream resets often come from the LLM service itself or an intermediary proxy.

Example fix

// before
client := &http.Client{} // default timeouts too tight for long streams

// after
client := &http.Client{
    Timeout: 0, // stream-managed
    Transport: &http.Transport{ResponseHeaderTimeout: 60 * time.Second, IdleConnTimeout: 90 * time.Second},
}
Defensive patterns

Strategy: retry

Try / catch

err := api.proxyLLMStream(c, payload)
if err != nil {
    if strings.Contains(err.Error(), "读取上游流式响应失败") {
        // truncated stream: notify client to regenerate
        c.SSEvent("error", "upstream stream interrupted")
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Mid-stream, res.Body.Read returns a non-EOF error: upstream LLM connection reset, provider timeout, TLS interruption, proxy between server and provider cutting the chunked response, or the provider aborting an over-long generation.

Common situations: Very long completions exceed provider/proxy stream limits; unstable network to the LLM endpoint; upstream rate-limit kills active streams; keep-alive idle timeouts on the outbound HTTP transport.

Related errors


AI-assisted analysis of flipped-aurora/gin-vue-admin@3136500ef3 (2026-08-31). Data as JSON: /api/errors/7a8243b98e04e8ee. Report an issue: GitHub.