vitessio/vitess · error
one or two tables must be specified
Error message
one or two tables must be specified
What it means
During CreateLookupVindex, prepareCreate validates the "tables" entry of the Vindex spec. A lookup vindex backfill can only operate against one table (single-table vindex) or two tables (source plus lookup target), so any other count — zero, or more than two — is rejected with this error.
Source
Thrown at go/vt/vtctl/workflow/lookup_vindex.go:130
for _, colVindex := range table.ColumnVindexes {
columnVindexByName[colVindex.Name] = colVindex
}
}
for vindexName, vindex := range specs.Vindexes {
vInfo, err := lv.validateAndGetVindexInfo(vindexName, vindex, specs.Tables)
if err != nil {
return nil, nil, nil, nil, err
}
if vindex.Owner == "" {
// If the vindex is unowned, we need to search for source table
// using a loop iterating over specs.Tables. And, as we have
// already validated if all vindexes are owned in case of multiple
// vindexes, so this case should be possible only when we are
// backfilling a single vindex. So, this approach can be used.
if len(specs.Tables) < 1 || len(specs.Tables) > 2 {
return nil, nil, nil, nil, errors.New("one or two tables must be specified")
}
vInfo.sourceTable, vInfo.sourceTableName, err = getSourceTable(specs.Tables, vInfo.targetTableName, vInfo.fromCols)
if err != nil {
return nil, nil, nil, nil, err
}
if vInfo.sourceTable == nil || vInfo.sourceTableName == "" {
return nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "source table not found for vindex %s", vInfo.name)
}
} else {
var ok bool
if vInfo.sourceTable, ok = specs.Tables[vindex.Owner]; !ok {
return nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "table owner not found for vindex %s", vInfo.name)
}
vInfo.sourceTableName = vindex.Owner
}
if len(specs.Vindexes) == 1 {
sourceVindexColumns, err = validateSourceTableAndGetVindexColumns(vInfo, vindex, keyspace)View on GitHub (pinned to 01a25a7d17)
Solutions
- Restructure the vschema so each CreateLookupVindex call's spec lists exactly one (or two) tables
- Issue one CreateLookupVindex per table for a lookup vindex shared by multiple tables
- Validate the vschema JSON (table count per vindex spec) before invoking the workflow
Example fix
// before (vschema spec)
"tables": {"t1": {}, "t2": {}, "t3": {}}
// after
"tables": {"t1": {"column_vindexes": [{"column": "c", "name": "lv"}]}} // one table per call Defensive patterns
Strategy: validation
Validate before calling
if len(spec.Tables) < 1 || len(spec.Tables) > 2 {
return fmt.Errorf("lookup vindex spec must reference one or two tables, got %d", len(spec.Tables))
} Type guard
func validTableCount(n int) bool { return n >= 1 && n <= 2 } Try / catch
if _, _, _, _, err := prepareCreate(ctx, env, ks, params, spec); err != nil {
if strings.Contains(err.Error(), "one or two tables must be specified") {
return fmt.Errorf("split the vschema spec per table: %w", err)
}
return err
} Prevention
- Issue one CreateLookupVindex per table for shared vindexes
- Lint the vschema JSON before applying
- Count the tables entries in a spec during pre-flight validation
When it happens
Trigger: Calling workflow.CreateLookupVindex where the parsed vschema spec's Tables slice has length 0 or > 2, typically a lookup vindex definition whose tables map references three or more tables, or omits tables entirely at this stage.
Common situations: Authoring a complex vschema with a shared lookup vindex across many tables and trying to Materialize/Create it without splitting per table; malformed JSON in the vschema spec.
Related errors
- at least one table must be specified
- vindex is in write-only mode
- vindex has no owner
- no sharded vschema was provided, so you will need to update
- cannot build vschema for keyspace %v: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/9ab2830c17d45a51.
Report an issue: GitHub.