vitessio/vitess · error

schemas are different: %s: %v differs from: %s: %v

Error message

schemas are different:
%s: %v
 differs from:
%s: %v

What it means

DiffSchema records this error when both SchemaDefinitions are non-nil but their DatabaseSchema strings (the CREATE DATABASE / default character set portion) differ. It shows the left and right database-level schema side by side.

Source

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

			sqlStrings = append(sqlStrings, strings.Join(lines, "\n"))
		}
	}

	return append(sqlStrings, createViewSQL...)
}

// DiffSchema generates a report on what's different between two SchemaDefinitions
// including views, but Vitess internal tables are ignored.
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))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Run the desired ALTER DATABASE statement on the mismatching tablet's MySQL so both database definitions match
  2. Re-create the database consistently and re-copy the schema
  3. Update provisioning tooling to use a single canonical CREATE DATABASE statement

Example fix

// before
-- tablet A: CREATE DATABASE vt_app CHARACTER SET utf8
-- tablet B: CREATE DATABASE vt_app CHARACTER SET utf8mb4
// after (on tablet B's MySQL)
ALTER DATABASE vt_app CHARACTER SET utf8;
Defensive patterns

Strategy: validation

Validate before calling

leftDb, rightDb := left.GetDatabaseSchema(), right.GetDatabaseSchema()
if leftDb != rightDb {
    log.Warn("database schema mismatch before diff", slog.String("left", leftDb), slog.String("right", rightDb))
}

Try / catch

er := concurrency.AllErrorRecorder{}
tmutils.DiffSchema("left", left, "right", right, &er)
if er.HasErrors() {
    for _, e := range er.Errors { log.Warn("schema diff", slog.Any("error", e)) }
    return er.Error()
}

Prevention

When it happens

Trigger: DiffSchema invoked on two tablets whose underlying MySQL databases were created with different definitions — e.g. different CREATE DATABASE charset/collation statements.

Common situations: Tablets provisioned at different times with different default charset (utf8 vs utf8mb4); manual ALTER DATABASE on one replica; primary migrated to a new cluster with a different database definition.

Related errors


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