{"record":{"id":"f8e3ac5a0a8b3789","repo":"flipped-aurora/gin-vue-admin","slug":"w-f8e3ac","errorCode":null,"errorMessage":"读取上游流式响应失败: %w","messagePattern":"读取上游流式响应失败: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/api/v1/system/sys_auto_code_sse.go","lineNumber":118,"sourceCode":"\t\treturn errors.New(\"当前响应不支持流式输出\")\n\t}\n\n\tprepareSSEHeaders(c)\n\tc.Status(http.StatusOK)\n\tflusher.Flush()\n\n\treader := bufio.NewReader(res.Body)\n\tlines := make([]string, 0, 8)\n\tblockCount := 0\n\n\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Info(\"LLMAutoSSE 开始读取上游流数据...\")\n\n\tfor {\n\t\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Debug(\"LLMAutoSSE 等待读取下一行...\")\n\t\tline, readErr := reader.ReadString('\\n')\n\t\tif readErr != nil && !errors.Is(readErr, io.EOF) {\n\t\t\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Field(\"已转发块数\", blockCount).Err(readErr).Error(\"LLMAutoSSE 读取上游流失败\")\n\t\t\treturn fmt.Errorf(\"读取上游流式响应失败: %w\", readErr)\n\t\t}\n\n\t\tline = strings.TrimRight(line, \"\\r\\n\")\n\t\tif line == \"\" {\n\t\t\tif len(lines) > 0 {\n\t\t\t\tblockCount++\n\t\t\t\tif blockCount <= 3 {\n\t\t\t\t\tlogger.WithCtx(c.Request.Context()).Mod(\"biz\").Field(\"block\", blockCount).Field(\"lines\", lines).Debug(\"LLMAutoSSE 转发 SSE 块\")\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err := emitSSEBlock(c, lines); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlines = lines[:0]\n\t\t} else {\n\t\t\tlines = append(lines, line)\n\t\t}\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/api/v1/system/sys_auto_code_sse.go#L100-L136","documentation":"In streaming mode the handler reads the upstream SSE body line-by-line with bufio.Reader.ReadString. If ReadString returns an error other than io.EOF mid-stream (connection reset, broken pipe, timeout), the read loop aborts with this wrapped error after logging how many blocks were already forwarded.","triggerScenarios":"During active SSE streaming, the upstream connection drops: res.Body read fails with a network error (not a clean EOF), e.g. upstream crash, idle-timeout kill, or LB connection teardown.","commonSituations":"Long generations exceed an intermediate proxy's idle timeout; upstream restarts during a stream; client network flakes; keep-alive limits on LB.","solutions":["Inspect the wrapped error for timeout vs connection-reset and fix accordingly (timeouts, keep-alive settings)","Increase idle/read timeouts on any proxy between the service and the LLM upstream","Implement resumable or retry logic at a higher level for long streams","Check upstream provider status; 5xx/stream aborts during generation are provider-side"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"line, readErr := reader.ReadString('\\n')\nif readErr != nil && !errors.Is(readErr, io.EOF) {\n    if isRetryableNetErr(readErr) { /* reconnect & resume stream */ }\n    return readErr\n}","preventionTips":["Configure LB/proxy idle timeouts above the max generation duration","Send SSE keep-alive pings upstream-side to keep connections warm","Surface partial results already forwarded before the drop"],"tags":["http","sse","streaming","network"],"backgroundTag":"stream-connection-dropped","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}