vitessio/vitess · error

invalid table name: %s

Error message

invalid table name: %s

What it means

Returned by vreplication controller plan building when a table name parsed from a routing rule or query construct is not a valid identifier.

Source

Thrown at go/vt/vttablet/tabletmanager/vreplication/controller_plan.go:186

	if ins == nil {
		return nil, errors.New("BUG: invalid nil INSERT statement found when building VReplication plan")
	}
	tableName, err := ins.Table.TableName()
	if err != nil {
		return nil, err
	}
	if tableName.Qualifier.String() != sidecar.GetName() && tableName.Qualifier.String() != sidecar.DefaultName {
		return nil, fmt.Errorf("invalid database name: %s", tableName.Qualifier.String())
	}
	switch tableName.Name.String() {
	case reshardingJournalTableName:
		return &controllerPlan{
			opcode: reshardingJournalQuery,
		}, nil
	case vreplicationTableName:
		// no-op
	default:
		return nil, fmt.Errorf("invalid table name: %s", tableName.Name.String())
	}
	if ins.Action != sqlparser.InsertAct {
		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
	}
	if ins.Ignore {
		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
	}
	if ins.Partitions != nil {
		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
	}
	if ins.OnDup != nil {
		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
	}
	rows, ok := ins.Rows.(sqlparser.Values)
	if !ok {
		return nil, fmt.Errorf("unsupported construct: %v", sqlparser.String(ins))
	}
	idPos := 0

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Restrict engine-driven INSERTs to _vt.vreplication (and _vt.resharding_journal).
  2. For internal tables like _vt.copy_state, execute the statement directly on the tablet's MySQL as the vt_dba user.
  3. Use the documented workflow commands (vtctldclient) instead of mutating internal tables.
  4. If a new sidecar table needs engine support, add a case in buildInsertPlan upstream.

Example fix

// before
insert into _vt.copy_state(vid, uid) values (1, 2)
// after — run on tablet MySQL directly, or use a supported table:
insert into _vt.vreplication(id, workflow) values (1, 'w')
Defensive patterns

Strategy: validation

Validate before calling

allowed := map[string]bool{"vreplication": true, "resharding_journal": true}
table := tableNameFromInsert(stmt)
if !allowed[table] {
    return fmt.Errorf("table %s is not engine-writable; use tablet MySQL directly", table)
}

Try / catch

plan, err := buildInsertPlan(ins, sidecar)
if err != nil && strings.Contains(err.Error(), "invalid table name") {
    return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "only _vt.vreplication and _vt.resharding_journal accept engine INSERTs")
}

Prevention

When it happens

Trigger: An INSERT statement against a sidecar table other than vreplication or resharding_journal — e.g. `insert into _vt.copy_state ...` sent through the vreplication engine's plan builder.

Common situations: Operators trying to hand-edit internal bookkeeping tables (copy_state, lag, etc.) via VExec; tooling that assumed all sidecar tables are writable through this path; scripts written for direct-MySQL use repointed at the vreplication engine.

Related errors


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