vitessio/vitess · error
%v has an extra view named %v
Error message
%v has an extra view named %v
What it means
Same leftover-tables loop as the extra-table error, but for TableView definitions: a view exists only on the left schema and is reported as 'an extra view named ...'. Unlike extra base tables, internal operation table filtering does NOT apply to views — any unmatched view is reported.
Source
Thrown at go/vt/mysqlctl/tmutils/schema.go:273
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))
}
}
if left.TableDefinitions[leftIndex].Type == TableView {
er.RecordError(fmt.Errorf("%v has an extra view named %v", leftName, left.TableDefinitions[leftIndex].Name))
}
leftIndex++
}
for rightIndex < len(right.TableDefinitions) {
if right.TableDefinitions[rightIndex].Type == TableBaseTable {
if !schema.IsInternalOperationTableName(right.TableDefinitions[rightIndex].Name) {
er.RecordError(fmt.Errorf("%v has an extra table named %v", rightName, right.TableDefinitions[rightIndex].Name))
}
}
if right.TableDefinitions[rightIndex].Type == TableView {
er.RecordError(fmt.Errorf("%v has an extra view named %v", rightName, right.TableDefinitions[rightIndex].Name))
}
rightIndex++
}
}
// DiffSchemaToArray diffs two schemas and return the schema diffs if there is any.
func DiffSchemaToArray(leftName string, left *tabletmanagerdatapb.SchemaDefinition, rightName string, right *tabletmanagerdatapb.SchemaDefinition) (result []string) {View on GitHub (pinned to 01a25a7d17)
Solutions
- Create the missing view on the other side (ApplySchema with the CREATE VIEW statement) or drop it on the side that has it.
- Ensure ApplySchema/SchemaReload was run on all tablets in both keyspaces/shards.
- Verify with SHOW FULL TABLES on both tablets which side is correct.
- Rerun validation to confirm the diff is gone.
Example fix
// before: view only on left vtctldclient ApplySchema --sql "CREATE VIEW v1 AS SELECT ..." ks // after: view exists on both sides
Defensive patterns
Strategy: validation
Validate before calling
for _, t := range left.TableDefinitions {
if t.Type == TableView { found := false; for _, r := range right.TableDefinitions { if r.Name == t.Name { found = true } }; if !found { return fmt.Errorf("missing view %s on right", t.Name) } }
} Type guard
func isView(td *tmutils.TableDefinition) bool { return td.Type == tmutils.TableView } Try / catch
err := tmutils.DiffSchema(ctx, left, right)
if err != nil { return fmt.Errorf("view/table set diverged: %w", err) } Prevention
- Ship CREATE VIEW statements together with table DDL in ApplySchema migrations
- Verify SHOW FULL TABLES parity after view deployments
When it happens
Trigger: Calling DiffSchema where left.TableDefinitions has a TableView entry with no counterpart on the right after the name walk.
Common situations: A view was created on the source keyspace only; a table was converted to a view on one shard; view definitions were excluded from a partial ApplySchema run; backup/restore across versions where views were introduced later.
Related errors
- empty create database statement for %v
- empty create table statement for %v
- schemas differ on table type for table %v: %s: %v differs f
- not allowed: deny-all security-policy enforced
- ReadFile cannot be called on read-write backup
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/fe90f0d32d71f3b7.
Report an issue: GitHub.