{"record":{"id":"9ab2830c17d45a51","repo":"vitessio/vitess","slug":"one-or-two-tables-must-be-specified","errorCode":null,"errorMessage":"one or two tables must be specified","messagePattern":"one or two tables must be specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/workflow/lookup_vindex.go","lineNumber":130,"sourceCode":"\t\tfor _, colVindex := range table.ColumnVindexes {\n\t\t\tcolumnVindexByName[colVindex.Name] = colVindex\n\t\t}\n\t}\n\n\tfor vindexName, vindex := range specs.Vindexes {\n\t\tvInfo, err := lv.validateAndGetVindexInfo(vindexName, vindex, specs.Tables)\n\t\tif err != nil {\n\t\t\treturn nil, nil, nil, nil, err\n\t\t}\n\n\t\tif vindex.Owner == \"\" {\n\t\t\t// If the vindex is unowned, we need to search for source table\n\t\t\t// using a loop iterating over specs.Tables. And, as we have\n\t\t\t// already validated if all vindexes are owned in case of multiple\n\t\t\t// vindexes, so this case should be possible only when we are\n\t\t\t// backfilling a single vindex. So, this approach can be used.\n\t\t\tif len(specs.Tables) < 1 || len(specs.Tables) > 2 {\n\t\t\t\treturn nil, nil, nil, nil, errors.New(\"one or two tables must be specified\")\n\t\t\t}\n\t\t\tvInfo.sourceTable, vInfo.sourceTableName, err = getSourceTable(specs.Tables, vInfo.targetTableName, vInfo.fromCols)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, nil, nil, err\n\t\t\t}\n\t\t\tif vInfo.sourceTable == nil || vInfo.sourceTableName == \"\" {\n\t\t\t\treturn nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, \"source table not found for vindex %s\", vInfo.name)\n\t\t\t}\n\t\t} else {\n\t\t\tvar ok bool\n\t\t\tif vInfo.sourceTable, ok = specs.Tables[vindex.Owner]; !ok {\n\t\t\t\treturn nil, nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, \"table owner not found for vindex %s\", vInfo.name)\n\t\t\t}\n\t\t\tvInfo.sourceTableName = vindex.Owner\n\t\t}\n\n\t\tif len(specs.Vindexes) == 1 {\n\t\t\tsourceVindexColumns, err = validateSourceTableAndGetVindexColumns(vInfo, vindex, keyspace)","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/workflow/lookup_vindex.go#L112-L148","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (vschema spec)\n\"tables\": {\"t1\": {}, \"t2\": {}, \"t3\": {}}\n// after\n\"tables\": {\"t1\": {\"column_vindexes\": [{\"column\": \"c\", \"name\": \"lv\"}]}}  // one table per call","handlingStrategy":"validation","validationCode":"if len(spec.Tables) < 1 || len(spec.Tables) > 2 {\n    return fmt.Errorf(\"lookup vindex spec must reference one or two tables, got %d\", len(spec.Tables))\n}","typeGuard":"func validTableCount(n int) bool { return n >= 1 && n <= 2 }","tryCatchPattern":"if _, _, _, _, err := prepareCreate(ctx, env, ks, params, spec); err != nil {\n    if strings.Contains(err.Error(), \"one or two tables must be specified\") {\n        return fmt.Errorf(\"split the vschema spec per table: %w\", err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["vindex","vschema","lookup-vindex","validation"],"backgroundTag":"invalid-vschema-spec","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}