flipped-aurora/gin-vue-admin · critical

调用上游大模型失败: %w

Error message

调用上游大模型失败: %w

What it means

streamLLMAsSSE in server/api/v1/system/sys_auto_code_sse.go first calls the service method LLMAutoStream(ctx, llm), which performs the outbound HTTP request to the upstream LLM and returns the *http.Response. Any error from that call (DNS failure, connection refused/refused TLS, request-context cancellation, client construction failure) is wrapped as fmt.Errorf("调用上游大模型失败: %w", err) — 'failed to call upstream LLM'. This error is thrown before any status-code check; it means the request itself never completed.

Source

Thrown at server/api/v1/system/sys_auto_code_sse.go:54

		llm = common.JSONMap{}
	}
	llm["response_mode"] = "streaming"
	logger.WithCtx(c.Request.Context()).Mod("biz").Field("mode", llm["mode"]).Info("LLMAutoSSE 收到请求")

	if err := autoApi.streamLLMAsSSE(c, llm); err != nil {
		logger.WithCtx(c.Request.Context()).Mod("biz").Err(err).Error("大模型 SSE 代理失败!")
		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 {

View on GitHub (pinned to 3136500ef3)

Solutions

  1. Check the wrapped %w error to distinguish connect-refused, DNS failure, timeout, or context canceled.
  2. Verify the LLM endpoint/base URL configuration and test connectivity from the server: curl the endpoint.
  3. Confirm outbound network/DNS from the deployment environment (containers often need proxy/env settings).
  4. If the endpoint is right but flaky, add retry with backoff around LLMAutoStream and surface a friendly SSE error event to the client.

Example fix

// before
LLM_BASE_URL=https://api.exampel.com/v1  # typo

// after
LLM_BASE_URL=https://api.example.com/v1  # curl -I $LLM_BASE_URL/models returns 200 first
Defensive patterns

Strategy: retry

Validate before calling

url := os.Getenv("LLM_BASE_URL")
if u, err := neturl.Parse(url); err != nil || u.Scheme == "" || u.Host == "" {
    return errors.New("LLM_BASE_URL is not a valid absolute URL")
}
conn, err := net.DialTimeout("tcp", hostFromURL(url), 3*time.Second)
if err != nil { return fmt.Errorf("LLM endpoint unreachable: %w", err) }
conn.Close()

Try / catch

if err := api.LLMAutoSSE(c); err != nil {
    var dnsErr *net.DNSError
    if errors.As(err, &dnsErr) {
        c.JSON(502, gin.H{"msg": "LLM endpoint DNS failure"})
        return
    }
    c.JSON(502, gin.H{"msg": "LLM upstream unavailable"})
}

Prevention

When it happens

Trigger: LLMAutoSSE -> streamLLMAsSSE, where LLMAutoStream fails to establish or complete the upstream HTTP request: wrong/unreachable LLM endpoint, DNS failure, TCP connect timeout, TLS handshake failure, or the request context was canceled while dialing.

Common situations: LLM base URL misconfigured (typo, http vs https), server has no outbound internet/DNS in a container, corporate firewall blocks the endpoint, provider changed its API host, or the Gin request was canceled before the upstream connected.

Related errors


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