{"record":{"id":"b7e7a70cd597966d","repo":"flipped-aurora/gin-vue-admin","slug":"error","errorCode":null,"errorMessage":"当前响应不支持流式输出","messagePattern":"当前响应不支持流式输出","errorType":"error_code","errorClass":null,"httpStatus":500,"severity":"error","filePath":"server/api/v1/system/sys_auto_code.go","lineNumber":181,"sourceCode":"\nfunc (autoApi *AutoCodeApi) proxyLLMStream(c *gin.Context, llm common.JSONMap) error {\n\tres, err := autoCodeService.LLMAutoStream(c.Request.Context(), llm)\n\tif err != nil {\n\t\treturn 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\tflusher, ok := c.Writer.(http.Flusher)\n\tif !ok {\n\t\treturn errors.New(\"当前响应不支持流式输出\")\n\t}\n\n\tcopyLLMStreamHeaders(c.Writer.Header(), res.Header)\n\tif c.Writer.Header().Get(\"Content-Type\") == \"\" {\n\t\tc.Writer.Header().Set(\"Content-Type\", \"text/event-stream; charset=utf-8\")\n\t}\n\tif c.Writer.Header().Get(\"Cache-Control\") == \"\" {\n\t\tc.Writer.Header().Set(\"Cache-Control\", \"no-cache\")\n\t}\n\tc.Writer.Header().Set(\"Connection\", \"keep-alive\")\n\tc.Writer.Header().Set(\"X-Accel-Buffering\", \"no\")\n\tc.Status(res.StatusCode)\n\tflusher.Flush()\n\n\tbuf := make([]byte, 32*1024)\n\tfor {\n\t\tn, readErr := res.Body.Read(buf)\n\t\tif n > 0 {","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/flipped-aurora/gin-vue-admin/blob/3136500ef380842b0eb6c4daa87c3f8a47fcf9e0/server/api/v1/system/sys_auto_code.go#L163-L199","documentation":"proxyLLMStream relays the upstream LLM SSE stream to the gin response. gin's ResponseWriter only supports Flush() when it implements http.Flusher; on some writers (e.g. wrapped/hijacked writers, or when a middleware replaced the writer) the assertion fails and the proxy aborts with this error.","triggerScenarios":"c.Writer.(http.Flusher) type assertion fails while proxying a successful (2xx) upstream LLM stream response.","commonSituations":"A middleware wraps the gin Context.Writer with a custom ResponseWriter that doesn't embed http.Flusher; running behind a setup that hijacks or wraps the connection; unusual test harness writers.","solutions":["Audit middleware for custom ResponseWriter wrappers and ensure they embed http.Flusher and forward Flush()","Remove/reorder middleware that replaces c.Writer on the LLM stream route","Test with the vanilla gin engine route to confirm the writer supports flushing"],"exampleFix":"// before\ntype wrappedWriter struct { gin.ResponseWriter }\n// after\ntype wrappedWriter struct {\n  gin.ResponseWriter // already implements Flusher; do not shadow Flush\n}","handlingStrategy":"validation","validationCode":"flusher, ok := c.Writer.(http.Flusher)\nif !ok {\n  // fail fast with a clear middleware hint\n  return fmt.Errorf(\"当前响应不支持流式输出: middleware %T replaced the writer\", c.Writer)\n}","typeGuard":null,"tryCatchPattern":"if err := proxyLLMStream(c, upstream); err != nil {\n  if strings.Contains(err.Error(), \"不支持流式输出\") {\n    logger.Error(\"ResponseWriter cannot flush; check middleware wrapping c.Writer\")\n  }\n  respondSSEError(c, err)\n}","preventionTips":["Keep custom gin ResponseWriter wrappers embedding gin.ResponseWriter","Review middleware ordering on SSE routes after every dependency upgrade","Add a unit test asserting c.Writer implements http.Flusher on SSE routes"],"tags":["gin","sse","streaming","backend","go"],"backgroundTag":"response-writer-not-flusher","analyzedSha":"3136500ef380842b0eb6c4daa87c3f8a47fcf9e0","analyzedAt":"2026-08-31T13:50:02.721Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}