vitessio/vitess · error

schemas differ on table %v: %s: %v differs from: %s: %v

Error message

schemas differ on table %v:
%s: %v
 differs from:
%s: %v

What it means

When both tablets have a table with the same name but the CREATE TABLE / view definition string differs, DiffSchema records this error showing each side's table schema. Internal operation tables are skipped. This is the core table-level schema drift detection used by vtctldclient Schema Diff workflows.

Source

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

				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))
			}
		}

		if left.TableDefinitions[leftIndex].Type != right.TableDefinitions[rightIndex].Type {
			if !schema.IsInternalOperationTableName(right.TableDefinitions[rightIndex].Name) {
				er.RecordError(fmt.Errorf("schemas differ on table type for table %v:\n%s: %v\n differs from:\n%s: %v", left.TableDefinitions[leftIndex].Name, leftName, left.TableDefinitions[leftIndex].Type, rightName, right.TableDefinitions[rightIndex].Type))
			}
		}

		leftIndex++
		rightIndex++
	}

	for leftIndex < len(left.TableDefinitions) {
		if left.TableDefinitions[leftIndex].Type == TableBaseTable {
			if !schema.IsInternalOperationTableName(left.TableDefinitions[leftIndex].Name) {
				er.RecordError(fmt.Errorf("%v has an extra table named %v", leftName, left.TableDefinitions[leftIndex].Name))
			}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Apply the correct ALTER TABLE (or CREATE VIEW) on the lagging tablet so the definitions match
  2. Use vtctldclient ApplySchema / controlled replication catch-up to propagate the DDL
  3. Run an online schema change tool across all shards to converge the schema
  4. Re-run the schema diff afterward to confirm zero differences

Example fix

// before
-- tablet A: orders has column `note VARCHAR(100)`
-- tablet B: orders lacks `note`
// after (on tablet B's MySQL)
ALTER TABLE orders ADD COLUMN note VARCHAR(100);
Defensive patterns

Strategy: validation

Validate before calling

for i, lt := range left.TableDefinitions {
    for _, rt := range right.TableDefinitions {
        if lt.Name == rt.Name && lt.Schema != rt.Schema {
            log.Warn("table schema drift", slog.String("table", lt.Name), slog.String("left", lt.Schema), slog.String("right", rt.Schema))
        }
    }
    _ = i
}

Try / catch

er := concurrency.AllErrorRecorder{}
tmutils.DiffSchema("left", left, "right", right, &er)
if er.HasErrors() {
    return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "table schema drift: %w", er.Error())
}
// proceed only when schemas are identical

Prevention

When it happens

Trigger: DiffSchema compares two TableDefinitions with equal names where left.Schema != right.Schema — different columns, indexes, charsets, or view definitions between tablets.

Common situations: A migration/ALTER TABLE was applied on the primary but a replica missed it; schema applied out of order across shards; manual column addition on one tablet; a view redefined on only one side.

Related errors


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