vitessio/vitess · error

usage: VDiff -- <keyspace>.<workflow> %s [%s|<UUID>]

Error message

usage: VDiff -- <keyspace>.<workflow> %s [%s|<UUID>]

What it means

commandVDiff2 parses the VDiff command line and constructs a usage error listing all valid actions and action args. It is returned whenever the argument count after `<keyspace>.<workflow>` is not 1 (legacy vdiff1 form), 2, or 3 — i.e. the VDiff subcommand invocation doesn't match any accepted shape.

Source

Thrown at go/vt/vtctl/vdiff2.go:81

	maxExtraRowsToCompare := subFlags.Int64("max_extra_rows_to_compare", 1000, "If there are collation differences between the source and target, you can have rows that are identical but simply returned in a different order from MySQL. We will do a second pass to compare the rows for any actual differences in this case and this flag allows you to control the resources used for this operation.")

	autoRetry := subFlags.Bool("auto-retry", true, "Should this vdiff automatically retry and continue in case of recoverable errors")
	checksum := subFlags.Bool("checksum", false, "Use row-level checksums to compare, not yet implemented")
	samplePct := subFlags.Int64("sample_pct", 100, "How many rows to sample, not yet implemented")
	verbose := subFlags.Bool("verbose", false, "Show verbose vdiff output in summaries")
	wait := subFlags.Bool("wait", false, "When creating or resuming a vdiff, wait for it to finish before exiting")
	waitUpdateInterval := subFlags.Duration("wait-update-interval", time.Duration(1*time.Minute), "When waiting on a vdiff to finish, check and display the current status this often")
	updateTableStats := subFlags.Bool("update-table-stats", false, "Update the table statistics, using ANALYZE TABLE, on each table involved in the VDiff during initialization. This will ensure that progress estimates are as accurate as possible -- but it does involve locks and can potentially impact query processing on the target keyspace.")

	if err := subFlags.Parse(args); err != nil {
		return err
	}
	format = strings.ToLower(format)

	var action vdiff.VDiffAction
	var actionArg string

	usage := fmt.Errorf("usage: VDiff -- <keyspace>.<workflow> %s [%s|<UUID>]", strings.Join(*(*[]string)(unsafe.Pointer(&vdiff.Actions)), "|"), strings.Join(vdiff.ActionArgs, "|"))
	switch subFlags.NArg() {
	case 1: // for backward compatibility with vdiff1
		action = vdiff.CreateAction
	case 2:
		action = vdiff.VDiffAction(strings.ToLower(subFlags.Arg(1)))
		if action != vdiff.CreateAction {
			return usage
		}
	case 3:
		action = vdiff.VDiffAction(strings.ToLower(subFlags.Arg(1)))
		actionArg = strings.ToLower(subFlags.Arg(2))
	default:
		return usage
	}
	if action == "" {
		return fmt.Errorf("invalid action '%s'; %s", subFlags.Arg(1), usage)
	}
	keyspace, workflowName, err := splitKeyspaceWorkflow(subFlags.Arg(0))

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use `vtctl VDiff -- <keyspace>.<workflow> <action> [<actionArg>|<UUID>]` — 1, 2, or 3 args only after the workflow.
  2. Check the exact action list: create, show, complete, abort, remove, restart, split, delete, etc. (joined with '|' in the usage message).
  3. Ensure `--` separates vtctl-level flags from VDiff args so subFlags receives the intended tokens.
  4. For vdiff1-style invocation, a single extra arg is still accepted (treated as create) — remove extra tokens rather than reformulating.

Example fix

// before
VDiff -- commerce.sell  show   all status
// after
VDiff -- commerce.sell show all
Defensive patterns

Strategy: validation

Validate before calling

validActions := []string{"create","show","complete","abort","remove","restart","split","delete"}
if len(args) > 3 || (len(args) >= 1 && !slices.Contains(validActions, strings.ToLower(args[0]))) {
    return fmt.Errorf("invalid VDiff invocation; usage: VDiff -- <keyspace>.<workflow> <action> [<actionArg>|<UUID>]")
}

Try / catch

if err := runVDiff(args); err != nil {
    if strings.Contains(err.Error(), "usage: VDiff") {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(2)
    }
}

Prevention

When it happens

Trigger: Calling `vtctl VDiff -- <keyspace>.<workflow>` with zero extra args, or more than 2 args after the workflow (e.g. an extra stray token), so the switch on subFlags.NArg() falls to default and returns the usage error.

Common situations: Copy-pasting vdiff1 syntax; forgetting that the action name is a separate argument (`VDiff ks.wf show` not `VDiff ks.wf show all extra`); shell splitting inserting unexpected tokens; missing the `--` separator so vtctl flags consume arguments.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


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