vitessio/vitess · error

stream send error: %v

Error message

stream send error: %v

What it means

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.

Source

Thrown at go/vt/dbconnpool/connection.go:115

	flds, err := dbc.Fields()
	if err != nil {
		return err
	}
	firstResult := &sqltypes.Result{Fields: flds}
	// If the query produced no result set but an OK packet (e.g. a CALL that
	// performs DML), carry its RowsAffected/InsertID/Info/SessionStateChanges
	// through so the streaming path reports them like the buffered ExecuteFetch
	// path does.
	if okRes := dbc.StreamOKResult(); okRes != nil {
		firstResult.RowsAffected = okRes.RowsAffected
		firstResult.InsertID = okRes.InsertID
		firstResult.InsertIDChanged = okRes.InsertIDChanged
		firstResult.Info = okRes.Info
		firstResult.SessionStateChanges = okRes.SessionStateChanges
	}
	err = callback(firstResult)
	if err != nil {
		return fmt.Errorf("stream send error: %v", err)
	}

	// then get all the rows, sending them as we reach a decent packet size
	// start with a pre-allocated array of 256 rows capacity
	qr := alloc()
	byteCount := 0
	for {
		row, err := dbc.FetchNext(nil)
		if err != nil {
			dbc.handleError(err)
			return err
		}
		if row == nil {
			break
		}
		qr.Rows = append(qr.Rows, row)
		for _, s := range row {
			byteCount += s.Len()

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. 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.
  2. Ensure the callback honors context cancellation and returns promptly when the caller's context is done.
  3. Check client-side (e.g. VTGate-to-app) connection stability and timeouts if disconnects are frequent.
  4. If the callback error is unexpected, add logging in the callback to capture why sending firstResult failed.

Example fix

// before
err = callback(firstResult)
if err != nil {
	return fmt.Errorf("stream send error: %v", err)
}
// after (caller side, tolerate client cancellation)
err = callback(firstResult)
if err != nil {
	if ctx.Err() != nil {
		return ctx.Err() // expected cancellation, not a stream bug
	}
	return fmt.Errorf("stream send error: %v", err)
}
Defensive patterns

Strategy: try-catch

Try / catch

err := poolConn.ExecuteStreamFetch(query, callback, alloc)
if err != nil {
	if strings.Contains(err.Error(), "stream send error") && ctx.Err() != nil {
		// client cancelled; treat as normal termination
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/70adf53979ae2226. Report an issue: GitHub.