vitessio/vitess · error

--force-cut-over-after is only valid in 'vitess' strategy. F

Error message

--force-cut-over-after is only valid in 'vitess' strategy. Found %v value in '%v' strategy

What it means

ParseDDLStrategy rejects the --force-cut-over-after parameter when the selected strategy is not 'vitess' (or 'online', an alias). That cutover-control parameter is only meaningful for vitess-managed online DDL, so any nonzero value with mysql/direct strategies errors.

Source

Thrown at go/vt/schema/ddl_strategy.go:148

	}
	if _, err := setting.CutOverThreshold(); err != nil {
		return nil, err
	}
	if _, err := setting.RetainArtifactsDuration(); err != nil {
		return nil, err
	}
	if _, err := setting.SessionVariables(); err != nil {
		return nil, err
	}
	cutoverAfter, err := setting.ForceCutOverAfter()
	if err != nil {
		return nil, err
	}
	switch setting.Strategy {
	case DDLStrategyVitess, DDLStrategyOnline:
	default:
		if cutoverAfter != 0 {
			return nil, fmt.Errorf("--force-cut-over-after is only valid in 'vitess' strategy. Found %v value in '%v' strategy", cutoverAfter, setting.Strategy)
		}
	}

	switch setting.Strategy {
	case DDLStrategyVitess, DDLStrategyOnline, DDLStrategyMySQL, DDLStrategyDirect:
		if opts := setting.RuntimeOptions(); len(opts) > 0 {
			return nil, fmt.Errorf("invalid flags for %v strategy: %s", setting.Strategy, strings.Join(opts, " "))
		}
	}

	return setting, nil
}

// isFlag return true when the given string is a CLI flag of the given name
func isFlag(s string, name string) bool {
	if s == "-"+name {
		return true
	}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove --force-cut-over-after from the ddl_strategy value, e.g. SET @@ddl_strategy='mysql'.
  2. Switch to strategy 'vitess' or 'online' if you actually want controlled cutover with --force-cut-over-after.
  3. Fix automation that injects vitess-specific flags into non-vitess strategies.
  4. Use valid syntax SET @@ddl_strategy='vitess --force-cut-over-after=24h' when intended.

Example fix

// before
SET @@ddl_strategy='mysql --force-cut-over-after=24h';
// after
SET @@ddl_strategy='vitess --force-cut-over-after=24h'; // or drop the flag
Defensive patterns

Strategy: validation

Validate before calling

s := schema.ParseDDLStrategy(strategyName)
if (strategy == "mysql" || strategy == "direct") && strings.Contains(strategyName, "--force-cut-over-after") {
  return errors.New("--force-cut-over-after requires vitess/online strategy")
}

Type guard

func cutoverAllowed(s *schema.DDLStrategySetting) bool {
  return s.Strategy == schema.DDLStrategyVitess || s.Strategy == schema.DDLStrategyOnline
}

Try / catch

setting, err := schema.ParseDDLStrategy(v)
if err != nil { return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "ddl_strategy invalid: %v", err) }

Prevention

When it happens

Trigger: SET @@ddl_strategy='mysql --force-cut-over-after=24h' or 'direct --force-cut-over-after=...' — a nonzero cutoverAfter parsed while setting.Strategy is mysql or direct.

Common situations: Copy-pasting a vitess strategy string and editing only the strategy token, forgetting to remove parameters; automation templates appending --force-cut-over-after to every strategy; misunderstanding that mysql/direct run native DDL so forced cutover is meaningless.

Related errors


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