{"record":{"id":"cb352405d1ef5428","repo":"flipped-aurora/gin-vue-admin","slug":"w-cb3524","errorCode":null,"errorMessage":"调用上游大模型失败: %w","messagePattern":"调用上游大模型失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/api/v1/system/sys_auto_code_sse.go","lineNumber":54,"sourceCode":"\t\tllm = common.JSONMap{}\n\t}\n\tllm[\"response_mode\"] = \"streaming\"\n\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Field(\"mode\", llm[\"mode\"]).Info(\"LLMAutoSSE 收到请求\")\n\n\tif err := autoApi.streamLLMAsSSE(c, llm); err != nil {\n\t\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Err(err).Error(\"大模型 SSE 代理失败!\")\n\t\tif c.Writer.Written() {\n\t\t\twriteLLMStreamError(c, err)\n\t\t\treturn\n\t\t}\n\t\tresponse.FailWithMessage(err.Error(), c)\n\t}\n}\n\nfunc (autoApi *AutoCodeApi) streamLLMAsSSE(c *gin.Context, llm common.JSONMap) error {\n\tres, err := autoCodeService.LLMAutoStream(c.Request.Context(), llm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"调用上游大模型失败: %w\", err)\n\t}\n\tdefer res.Body.Close()\n\n\tif res.StatusCode < 200 || res.StatusCode >= 300 {\n\t\tbody, readErr := io.ReadAll(res.Body)\n\t\tif readErr != nil {\n\t\t\treturn fmt.Errorf(\"上游大模型流式服务返回非 2xx: status=%d content-type=%s read-body-err=%w\", res.StatusCode, res.Header.Get(\"Content-Type\"), readErr)\n\t\t}\n\t\treturn fmt.Errorf(\"上游大模型流式服务返回非 2xx: status=%d content-type=%s body=%s\", res.StatusCode, res.Header.Get(\"Content-Type\"), previewResponseBody(body))\n\t}\n\n\tct := res.Header.Get(\"Content-Type\")\n\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Field(\"status\", res.StatusCode).Field(\"content-type\", ct).Info(\"LLMAutoSSE 上游返回成功，开始 SSE 流式转发\")\n\n\t// 如果上游返回的不是 SSE 流（可能是 blocking 模式返回的 JSON），直接读取并转发\n\tif !strings.Contains(ct, \"text/event-stream\") && !strings.Contains(ct, \"text/plain\") {\n\t\tbody, readErr := io.ReadAll(res.Body)\n\t\tif readErr != nil {","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/api/v1/system/sys_auto_code_sse.go#L36-L72","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the wrapped %w error to distinguish connect-refused, DNS failure, timeout, or context canceled.","Verify the LLM endpoint/base URL configuration and test connectivity from the server: curl the endpoint.","Confirm outbound network/DNS from the deployment environment (containers often need proxy/env settings).","If the endpoint is right but flaky, add retry with backoff around LLMAutoStream and surface a friendly SSE error event to the client."],"exampleFix":"// before\nLLM_BASE_URL=https://api.exampel.com/v1  # typo\n\n// after\nLLM_BASE_URL=https://api.example.com/v1  # curl -I $LLM_BASE_URL/models returns 200 first","handlingStrategy":"retry","validationCode":"url := os.Getenv(\"LLM_BASE_URL\")\nif u, err := neturl.Parse(url); err != nil || u.Scheme == \"\" || u.Host == \"\" {\n    return errors.New(\"LLM_BASE_URL is not a valid absolute URL\")\n}\nconn, err := net.DialTimeout(\"tcp\", hostFromURL(url), 3*time.Second)\nif err != nil { return fmt.Errorf(\"LLM endpoint unreachable: %w\", err) }\nconn.Close()","typeGuard":null,"tryCatchPattern":"if err := api.LLMAutoSSE(c); err != nil {\n    var dnsErr *net.DNSError\n    if errors.As(err, &dnsErr) {\n        c.JSON(502, gin.H{\"msg\": \"LLM endpoint DNS failure\"})\n        return\n    }\n    c.JSON(502, gin.H{\"msg\": \"LLM upstream unavailable\"})\n}","preventionTips":["Validate LLM base URL and credentials at startup with a smoke request","Ensure egress/DNS is allowed from the deployment network","Use retry-with-backoff for dial failures","Prefer NewRequestWithContext so cancellations are distinguishable"],"tags":["network","http","llm","connection","gin-vue-admin"],"backgroundTag":"upstream-request-failed","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}