{"record":{"id":"8b645f367d8f706f","repo":"vitessio/vitess","slug":"either-source-or-target-shards-are-missing","errorCode":null,"errorMessage":"either source or target shards are missing","messagePattern":"either source or target shards are missing","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtctl/workflow/utils.go","lineNumber":443,"sourceCode":"\n\tif len(targets) == 0 {\n\t\treturn nil, fmt.Errorf(\"%w in keyspace %s for %s\", ErrNoStreams, targetKeyspace, workflow)\n\t}\n\n\treturn &TargetInfo{\n\t\tTargets:         targets,\n\t\tFrozen:          frozen,\n\t\tOptCells:        optCells,\n\t\tOptTabletTypes:  optTabletTypes,\n\t\tWorkflowType:    workflowType,\n\t\tWorkflowSubType: workflowSubType,\n\t\tOptions:         &options,\n\t}, nil\n}\n\nfunc getSourceAndTargetKeyRanges(sourceShards, targetShards []string) (*topodatapb.KeyRange, *topodatapb.KeyRange, error) {\n\tif len(sourceShards) == 0 || len(targetShards) == 0 {\n\t\treturn nil, nil, errors.New(\"either source or target shards are missing\")\n\t}\n\n\tgetKeyRange := func(shard string) (*topodatapb.KeyRange, error) {\n\t\tkrs, err := key.ParseShardingSpec(shard)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn krs[0], nil\n\t}\n\n\t// Happily string sorting of shards also sorts them in the ascending order of key\n\t// ranges in vitess.\n\tsort.Strings(sourceShards)\n\tsort.Strings(targetShards)\n\tgetFullKeyRange := func(shards []string) (*topodatapb.KeyRange, error) {\n\t\t// Expect sorted shards.\n\t\tkr1, err := getKeyRange(shards[0])\n\t\tif err != nil {","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtctl/workflow/utils.go#L425-L461","documentation":"Validation of a VReplication workflow (e.g. MoveTables completion validation) requires both source and target shard lists to be non-empty so their sharding key ranges can be parsed. If either list is empty, getSourceAndTargetKeyRanges rejects the call before any shard comparison is possible.","triggerScenarios":"Calling isPartialMoveTables / workflow validation with an empty sourceShards or targetShards slice — typically the ShardSet was never populated on the workflow, or a caller passed empty shard specs.","commonSituations":"Programmatic use of the workflow API with shards not yet loaded from topology, a MoveTables workflow created against a keyspace with zero shards, or a typo/mis-configuration leaving the shard list unset.","solutions":["Verify the workflow was created correctly with explicit source/target shards (MoveTables --source or same-keyspace shard specs)","Check the keyspace actually has shards in topology (vtctldclient GetTablets / GetShards) before validating completion","Fix the caller so it only invokes isPartialMoveTables after ShardSet is populated"],"exampleFix":"// before: empty shard list\nsourceShards := []string{}\nkr, _, err := getSourceAndTargetKeyRanges(sourceShards, targetShards)\n// after: guard at the caller\nif len(sourceShards) == 0 || len(targetShards) == 0 {\n    return fmt.Errorf(\"workflow has no source/target shards loaded\")\n}\nkr, _, err := getSourceAndTargetKeyRanges(sourceShards, targetShards)","handlingStrategy":"validation","validationCode":"if len(workflow.SourceShards) == 0 || len(workflow.TargetShards) == 0 {\n    return errors.New(\"workflow must have non-empty source and target shard sets before validation\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always create workflows with explicit shard specs","Confirm shard lists are loaded from topo before calling validation helpers","Add unit tests covering empty shard-set inputs"],"tags":["vreplication","validation","sharding"],"backgroundTag":"missing-shard-spec","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}