{"record":{"id":"70adf53979ae2226","repo":"vitessio/vitess","slug":"stream-send-error-v","errorCode":null,"errorMessage":"stream send error: %v","messagePattern":"stream send error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/dbconnpool/connection.go","lineNumber":115,"sourceCode":"\tflds, err := dbc.Fields()\n\tif err != nil {\n\t\treturn err\n\t}\n\tfirstResult := &sqltypes.Result{Fields: flds}\n\t// If the query produced no result set but an OK packet (e.g. a CALL that\n\t// performs DML), carry its RowsAffected/InsertID/Info/SessionStateChanges\n\t// through so the streaming path reports them like the buffered ExecuteFetch\n\t// path does.\n\tif okRes := dbc.StreamOKResult(); okRes != nil {\n\t\tfirstResult.RowsAffected = okRes.RowsAffected\n\t\tfirstResult.InsertID = okRes.InsertID\n\t\tfirstResult.InsertIDChanged = okRes.InsertIDChanged\n\t\tfirstResult.Info = okRes.Info\n\t\tfirstResult.SessionStateChanges = okRes.SessionStateChanges\n\t}\n\terr = callback(firstResult)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"stream send error: %v\", err)\n\t}\n\n\t// then get all the rows, sending them as we reach a decent packet size\n\t// start with a pre-allocated array of 256 rows capacity\n\tqr := alloc()\n\tbyteCount := 0\n\tfor {\n\t\trow, err := dbc.FetchNext(nil)\n\t\tif err != nil {\n\t\t\tdbc.handleError(err)\n\t\t\treturn err\n\t\t}\n\t\tif row == nil {\n\t\t\tbreak\n\t\t}\n\t\tqr.Rows = append(qr.Rows, row)\n\t\tfor _, s := range row {\n\t\t\tbyteCount += s.Len()","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/dbconnpool/connection.go#L97-L133","documentation":"ExecuteStreamFetch in dbconnpool wraps any error returned by the caller-provided callback that processes the first result of a streaming query. The SQL execution itself succeeded, but the consumer's callback (which sends the initial result fields to the client) failed, so the streaming fetch is aborted with this wrapper error preserving the original cause.","triggerScenarios":"Calling ExecuteStreamFetch (via streamOnce, e.g. from VTGate streaming query paths) with a callback that returns a non-nil error when handling firstResult — e.g. the downstream client connection was closed or the callback failed to serialize/send the initial fields.","commonSituations":"Client disconnects mid-query so the callback's write to the client fails; serialization errors on the initial result; context cancellation inside the callback during long-running streamed queries.","solutions":["Inspect the wrapped %v cause: if the underlying error is a broken pipe / client disconnect, it is benign — handle client cancellation upstream instead of treating it as a query failure.","Ensure the callback honors context cancellation and returns promptly when the caller's context is done.","Check client-side (e.g. VTGate-to-app) connection stability and timeouts if disconnects are frequent.","If the callback error is unexpected, add logging in the callback to capture why sending firstResult failed."],"exampleFix":"// before\nerr = callback(firstResult)\nif err != nil {\n\treturn fmt.Errorf(\"stream send error: %v\", err)\n}\n// after (caller side, tolerate client cancellation)\nerr = callback(firstResult)\nif err != nil {\n\tif ctx.Err() != nil {\n\t\treturn ctx.Err() // expected cancellation, not a stream bug\n\t}\n\treturn fmt.Errorf(\"stream send error: %v\", err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := poolConn.ExecuteStreamFetch(query, callback, alloc)\nif err != nil {\n\tif strings.Contains(err.Error(), \"stream send error\") && ctx.Err() != nil {\n\t\t// client cancelled; treat as normal termination\n\t\treturn nil\n\t}\n\treturn err\n}","preventionTips":["Always check ctx.Err() in the callback and return early on cancellation","Log the wrapped cause to distinguish client disconnects from real send failures","Keep the callback lightweight so it cannot fail on serialization edge cases"],"tags":["streaming","query-execution","callback"],"backgroundTag":"stream-send-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}