{"record":{"id":"e8a37d846565e4bd","repo":"vitessio/vitess","slug":"err-error-fprintf-write-failure","errorCode":null,"errorMessage":"err.Error() (Fprintf write failure)","messagePattern":"err\\.Error\\(\\) \\(Fprintf write failure\\)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"go/vt/vtgate/executor_scatter_stats.go","lineNumber":138,"sourceCode":"\t\thttp.Error(w, err.Error(), 500)\n\t\treturn\n\t}\n\n\tt := template.New(\"template\")\n\tt, err = t.Parse(statsHTML)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), 500)\n\t\treturn\n\t}\n\n\terr = t.Execute(w, results)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), 500)\n\t}\n\n\t_, err = fmt.Fprintf(w, \"Percentage of time spent on scatter queries: %2.2f%%\", results.PercentTimeScatter)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), 500)\n\t}\n}\n\nconst statsHTML = `\n<thead>\n\t<tr>\n\t\t<th>Query</th>\n\t\t<th># of executions</th>\n\t\t<th>Avg time/query</th>\n\t\t<th>% time of reads</th>\n\t\t<th>% time of scatters</th>\n\t\t<th>% of reads</th>\n\t\t<th>% of scatters</th>\n\t</tr>\n</thead>\n{{range .Items}}\n<tr class=\"medium\">\n\t<td>{{.Query}}</td>","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/executor_scatter_stats.go#L120-L156","documentation":"After executing the stats template, WriteScatterStats appends a summary line via fmt.Fprintf to the response. If that write fails (client gone, connection reset), the raw error is passed to http.Error with a 500. Because headers/body are already sent, the 500 cannot change the status code; the error text is effectively only visible in logs or as garbage in the stream.","triggerScenarios":"Calling the /scatterStats debug endpoint when the HTTP connection is broken or closed before the final Fprintf write completes.","commonSituations":"Client (curl/browser/monitoring scraper) disconnects during streaming of the stats page; response already committed so http.Error is a no-op on status.","solutions":["Retry the debug request with a stable connection","Check vtgate logs for the write error; treat client-disconnect errors as benign noise","Increase any client/proxy read timeouts that cut the response short"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"resp, err := http.Get(\"http://vtgate:15001/scatterStats\")\nif err != nil {\n\t// connection failed before write; use fallback stats source\n}","typeGuard":null,"tryCatchPattern":"resp, err := http.Get(url)\nif err != nil {\n\treturn err\n}\ndefer resp.Body.Close()\nbody, err := io.ReadAll(resp.Body)\nif err != nil {\n\tlog.Warn(\"partial scatterStats read\", slog.Any(\"error\", err))\n}","preventionTips":["Use adequate client timeouts so the response finishes streaming","Reconnect-and-retry scrapers instead of dropping mid-response","Treat trailing-write errors on already-committed responses as benign","Pull PercentTimeScatter from metrics endpoints when available"],"tags":["http","vtgate","debug-endpoint","write-failure"],"backgroundTag":"http-response-write-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}