vitessio/vitess · error

cannot build a row streamer plan for the %s table as a lastp

Error message

cannot build a row streamer plan for the %s table as a lastpk value was provided (%v) and the number of primary key values within it (%d) does not match the number of primary key columns in the table (%d)

What it means

When resuming a copy phase, the saved lastpk must have exactly one value per primary key column of the table. buildSelect rejects the plan when the counts differ, because the WHERE clause (col1 = ? and col2 > ?) or (col1 > ?) cannot be constructed correctly.

Source

Thrown at go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go:293

	// If we know the index name that we should be using then tell MySQL
	// to use it if possible. This helps to ensure that we are able to
	// leverage the ordering from the index itself and avoid having to
	// do a FILESORT of all the results. This index should contain all
	// of the PK columns which are used in the ORDER BY clause below.
	var indexHint string
	// If we're pushing down any expressions, we need to let the optimizer
	// choose the best index to use.
	if st.PKIndexName != "" && len(rs.plan.whereExprsToPushDown) == 0 {
		escapedPKIndexName, err := sqlescape.EnsureEscaped(st.PKIndexName)
		if err != nil {
			return "", err
		}
		indexHint = fmt.Sprintf(" force index (%s)", escapedPKIndexName)
	}
	buf.Myprintf(" from %v%s", sqlparser.NewIdentifierCS(rs.plan.Table.Name), indexHint)
	if len(rs.lastpk) != 0 { // We're in the Nth copy phase cycle and need to resume
		if len(rs.lastpk) != len(rs.pkColumns) {
			return "", fmt.Errorf("cannot build a row streamer plan for the %s table as a lastpk value was provided (%v) and the number of primary key values within it (%d) does not match the number of primary key columns in the table (%d)",
				st.Name, rs.lastpk, len(rs.lastpk), len(rs.pkColumns))
		}
		buf.WriteString(" where ")
		// This closure handles the case for composite PKs. For example,
		// if lastpk was (1,2), the where clause would be:
		// (col1 = 1 and col2 > 2) or (col1 > 1).
		// A tuple inequality like (col1,col2) > (1,2) ends up
		// being a full table scan for MySQL.
		addLastPKExpressions := func() {
			prefix := ""
			for lastcol, pkCol := range slices.Backward(rs.pkColumns) {
				buf.Myprintf("%s(", prefix)
				prefix = " or "
				for i, pk := range rs.pkColumns[:lastcol] {
					buf.Myprintf("%v = ", sqlparser.NewIdentifierCI(rs.plan.Table.Fields[pk].Name))
					rs.lastpk[i].EncodeSQL(buf)
					buf.Myprintf(" and ")
				}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check the table's current PK column count (SHOW INDEX/SHOW CREATE TABLE) and compare with the checkpointed lastpk in the workflow's vgtid.
  2. If the PK changed, reset/restart the workflow's copy phase (or correct the TableLastPK) so lastpk is regenerated against the current schema.
  3. Do not hand-edit checkpoints; use vtctld workflow restart/copy-phase reset so lastpk is rebuilt from live schema.
Defensive patterns

Strategy: validation

Validate before calling

pkCols := getPrimaryKeyColumns(tableName)
lastpk := checkpoint.TableLastPK[tableName].Lastpk
if lastpk != nil && len(lastpk.Rows) > 0 && len(lastpk.Rows[0]) != len(pkCols) {
    return fmt.Errorf("stale lastpk for %s: %d values vs %d pk cols; reset copy phase", tableName, len(lastpk.Rows[0]), len(pkCols))
}

Try / catch

err := stream(ctx, filter, tablePKs)
if err != nil && strings.Contains(err.Error(), "does not match the number of primary key columns") {
    // drop the stale checkpoint and restart the copy phase
    restartWorkflowCopyPhase(ctx, workflowID)
}

Prevention

When it happens

Trigger: Calling Stream/VStreamRows with a TableLastPK whose lastpk values count differs from the table's PK column count — e.g. lastpk saved against an older schema whose PK had a different number of columns.

Common situations: Workflow checkpoint (vgtid TableLastPK) written before an ALTER TABLE changed the primary key; copying lastpk state between tables with different PKs; manual editing of VReplication state.

Related errors


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