vitessio/vitess · warning

VStreamer is not open

Error message

VStreamer is not open

What it means

tableStreamer.newRowStreamer re-checks the VStreamer engine's isOpen flag before spawning each per-table row streamer. Because table streaming is long-lived, the engine may have been closed (or not yet fully open) between stream creation and this per-row-stream creation; when isOpen is 0 it returns "VStreamer is not open".

Source

Thrown at go/vt/vttablet/tabletserver/vstreamer/tablestreamer.go:157

	log.Info(fmt.Sprintf("Found %d tables to stream: %s", len(ts.tables), strings.Join(ts.tables, ", ")))
	for _, tableName := range ts.tables {
		log.Info("Streaming table " + tableName)
		if err := ts.streamTable(ts.ctx, tableName); err != nil {
			log.Error(fmt.Sprintf("Streaming table %s failed: %v", tableName, err))
			return err
		}
		log.Info("Finished streaming table " + tableName)
	}
	log.Info(fmt.Sprintf("Finished streaming %d tables", len(ts.tables)))
	return nil
}

func (ts *tableStreamer) newRowStreamer(ctx context.Context, query string, lastpk []sqltypes.Value,
	send func(*binlogdatapb.VStreamRowsResponse) error,
) (*rowStreamer, func(), error) {
	vse := ts.vse
	if atomic.LoadInt32(&vse.isOpen) == 0 {
		return nil, nil, errors.New("VStreamer is not open")
	}
	vse.mu.Lock()
	defer vse.mu.Unlock()

	rowStreamer := newRowStreamer(ctx, vse.env.Config().DB.FilteredWithDB(), vse.se, query, lastpk, vse.lvschema,
		send, vse, RowStreamerModeAllTables, ts.snapshotConn, ts.options)

	idx := vse.streamIdx
	vse.rowStreamers[idx] = rowStreamer
	vse.streamIdx++
	// Now that we've added the stream, increment wg.
	// This must be done before releasing the lock.
	vse.wg.Add(1)

	// Remove stream from map and decrement wg when it ends.
	cancel := func() {
		vse.mu.Lock()
		defer vse.mu.Unlock()

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Treat as a benign shutdown signal: the stream should be cancelled/restarted — VReplication workflows will retry automatically.
  2. If seen outside shutdowns, verify the tabletserver/VStreamer engine stayed open (check startup/shutdown logs).
  3. Retry the VStream/workflow once the tablet is fully healthy.

Example fix

// before
rs, cancel, err := ts.newRowStreamer(ctx, query, lastpk, send) // fails mid-shutdown
// after
if err != nil && err.Error() == "VStreamer is not open" { return gracefulStop() } // handle engine shutdown
Defensive patterns

Strategy: try-catch

Type guard

func engineShutdownErr(err error) bool { return err != nil && strings.Contains(err.Error(), "VStreamer is not open") }

Try / catch

rs, cancel, err := ts.newRowStreamer(ctx, query, lastpk, send)
if engineShutdownErr(err) {
  log.Info("VStreamer engine closed mid-stream; stopping table stream gracefully")
  return nil, nil, nil
}

Prevention

When it happens

Trigger: A running tableStreamer (streamTable / multi-table copy) calling newRowStreamer after Engine.Close, or before the engine finished opening; recursively invoked as newRowStreamer creates child row streamers.

Common situations: vttablet shutdown or tabletserver restart while a VReplication copy phase is actively streaming tables; cancelling VReplication workflows racing stream creation.

Related errors


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