vitessio/vitess · error

%v has an extra table named %v

Error message

%v has an extra table named %v

What it means

During the sorted merge of TableDefinitions, when the left tablet has a table whose name sorts before the right's current table, the left side has a table the right side lacks. This error records the extra table name — unless it is a Vitess internal operation table, which is silently skipped.

Source

Thrown at go/vt/mysqlctl/tmutils/schema.go:234

func DiffSchema(leftName string, left *tabletmanagerdatapb.SchemaDefinition, rightName string, right *tabletmanagerdatapb.SchemaDefinition, er concurrency.ErrorRecorder) {
	if left == nil && right == nil {
		return
	}
	if left == nil || right == nil {
		er.RecordError(fmt.Errorf("schemas are different:\n%s: %v, %s: %v", leftName, left, rightName, right))
		return
	}
	if left.DatabaseSchema != right.DatabaseSchema {
		er.RecordError(fmt.Errorf("schemas are different:\n%s: %v\n differs from:\n%s: %v", leftName, left.DatabaseSchema, rightName, right.DatabaseSchema))
	}

	leftIndex := 0
	rightIndex := 0
	for leftIndex < len(left.TableDefinitions) && rightIndex < len(right.TableDefinitions) {
		// extra table on the left side
		if left.TableDefinitions[leftIndex].Name < right.TableDefinitions[rightIndex].Name {
			if !schema.IsInternalOperationTableName(left.TableDefinitions[leftIndex].Name) {
				er.RecordError(fmt.Errorf("%v has an extra table named %v", leftName, left.TableDefinitions[leftIndex].Name))
			}
			leftIndex++
			continue
		}

		// extra table on the right side
		if left.TableDefinitions[leftIndex].Name > right.TableDefinitions[rightIndex].Name {
			if !schema.IsInternalOperationTableName(right.TableDefinitions[rightIndex].Name) {
				er.RecordError(fmt.Errorf("%v has an extra table named %v", rightName, right.TableDefinitions[rightIndex].Name))
			}
			rightIndex++
			continue
		}

		// same name, let's see content
		if left.TableDefinitions[leftIndex].Schema != right.TableDefinitions[rightIndex].Schema {
			if !schema.IsInternalOperationTableName(left.TableDefinitions[leftIndex].Name) {
				er.RecordError(fmt.Errorf("schemas differ on table %v:\n%s: %v\n differs from:\n%s: %v", left.TableDefinitions[leftIndex].Name, leftName, left.TableDefinitions[leftIndex].Schema, rightName, right.TableDefinitions[rightIndex].Schema))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Apply the missing CREATE TABLE on the right tablet (via controlled replication catch-up or manual apply)
  2. Or drop the extra table on the left tablet if it is stale/scratch
  3. Repair replication so future DDL propagates to both tablets

Example fix

// before
-- tablet A has table tmp_migration_state; tablet B does not
// after (on tablet A's MySQL)
DROP TABLE tmp_migration_state;
Defensive patterns

Strategy: validation

Validate before calling

leftNames := tableNames(left)
rightNames := tableNames(right)
if extra := missing(leftNames, rightNames); len(extra) > 0 {
    log.Warn("tables missing on right", slog.Any("tables", extra))
}

Try / catch

er := concurrency.AllErrorRecorder{}
tmutils.DiffSchema("left", left, "right", right, &er)
if er.HasErrors() {
    return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "schema drift detected: %w", er.Error())
}

Prevention

When it happens

Trigger: DiffSchema finds left.TableDefinitions contains a table name absent from right.TableDefinitions (left name < right name during the merge walk).

Common situations: A table was created only on one replica (replication broken or skipped DDL); a leftover scratch table on one tablet; an online schema change left its old ghost table on one side.

Related errors


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