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
- Set a valid starting position (pos/startPos GTID) on the stream so setStreamStartPosition can seed uvs.pos
- Ensure the workflow includes at least one table to copy so plans are built
- 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
- Always start workflows via vtctldclient Workflow start rather than raw inserts
- Confirm the workflow recorded a valid pos after copy phase completes
- Validate table filters resolve to non-empty lists before creating the workflow
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
- VStreamer is not open
- VStreamer is not open
- partial row image encountered: ensure binlog_row_image is se
- failed to instantiate throttler: %v
- no binlog player client factory named %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/8973cc64874dd3ba.
Report an issue: GitHub.