vitessio/vitess · error

stream needs a position or a table to copy

Error message

stream needs a position or a table to copy

What it means

The uvstreamer cannot start because it has neither a valid starting GTID position nor any table copy plans. A vreplication stream must either resume from a position or copy at least one table; with both missing there is nothing to stream. This guard in init() prevents launching a stream that would produce no events.

Source

Thrown at go/vt/vttablet/tabletserver/vstreamer/uvstreamer.go:440

// 2. TablePKs nil, startPos empty => full table copy of tables matching filter
// 3. TablePKs not nil, startPos empty => table copy (for pks > lastPK)
// 4. TablePKs not nil, startPos set => run catchup from startPos, then table copy  (for pks > lastPK)
//
// If table copy phase should run based on one of the previous states, then only copy the tables in
// TablesToCopy list.
func (uvs *uvstreamer) init() error {
	if uvs.startPos == "" /* full copy */ || len(uvs.inTablePKs) > 0 /* resume copy */ {
		if err := uvs.buildTablePlan(); err != nil {
			return err
		}
	}
	if uvs.startPos != "" {
		if err := uvs.setStreamStartPosition(); err != nil {
			return err
		}
	}
	if uvs.pos.IsZero() && (len(uvs.plans) == 0) {
		return errors.New("stream needs a position or a table to copy")
	}
	return nil
}

// Stream streams binlog events.
func (uvs *uvstreamer) Stream() error {
	log.Info("Stream() called")
	if err := uvs.init(); err != nil {
		return err
	}
	if len(uvs.plans) > 0 {
		log.Info("TablePKs is not nil: starting vs.copy()")
		if err := uvs.copy(uvs.ctx); err != nil {
			log.Info(fmt.Sprintf("uvstreamer.Stream() copy returned with err %s", err))
			uvs.vse.errorCounts.Add("Copy", 1)
			return err
		}
		if err := uvs.allCopyComplete(); err != nil {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Set a valid starting position (pos/startPos GTID) on the stream so setStreamStartPosition can seed uvs.pos
  2. Ensure the workflow includes at least one table to copy so plans are built
  3. Inspect the _vt.vreplication row for the stream and fix its message/pos, then restart the workflow with Workflow <ks>.<wf> start

Example fix

// before: stream row with empty pos and no tables
// after: restart with explicit position or correct table filter
workflow := "ks.mt1"
vtctldclient Workflow --keyspace ks moveTables --workflow mt1 --tables a,b start
Defensive patterns

Strategy: validation

Validate before calling

-- before creating/restarting a stream
SELECT workflow, pos, message FROM _vt.vreplication WHERE workflow='<wf>';
-- pos must be non-empty OR the workflow must list tables to copy

Try / catch

if err := streamer.Stream(); err != nil {
	if strings.Contains(err.Error(), "needs a position or a table") {
		// fix workflow config: set pos or table list, then restart
	}
	return err
}

Prevention

When it happens

Trigger: Calling uvstreamer.Stream() when uvs.pos is zero and uvs.plans is empty — e.g. a VReplication stream created without a stop/start position and with an empty or fully filtered table list, or a plan-building failure that silently left plans empty.

Common situations: Misconfigured MoveTables/Reshard workflows where the source table list resolved to nothing; a workflow whose copy phase finished and whose starting position was lost or never recorded; manually inserting vreplication rows with an empty pos.

Related errors


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