vitessio/vitess · error · ErrMySQLShellPreCheck

%w: mysql-shell needs to restore with updateGtidSet set to "

Error message

%w: mysql-shell needs to restore with updateGtidSet set to "replace" to work with Vitess

What it means

Vitess requires MySQL Shell's util.loadDump to run with updateGtidSet set to "replace" so the restored dump's GTID set is handled correctly for replication. restorePreCheck() wraps ErrMySQLShellPreCheck when the load flags lack the key or set it to anything other than "replace".

Source

Thrown at go/vt/mysqlctl/mysqlshellbackupengine.go:511

		}
	}

	return nil
}

func (be *MySQLShellBackupEngine) restorePreCheck(ctx context.Context, params RestoreParams) (shouldDeleteUsers bool, err error) {
	if mysqlShellFlags == "" {
		return shouldDeleteUsers, fmt.Errorf("%w: at least the --js flag is required in the value of the flag --mysql-shell-flags", ErrMySQLShellPreCheck)
	}

	loadFlags := map[string]any{}
	err = json.Unmarshal([]byte(mysqlShellLoadFlags), &loadFlags)
	if err != nil {
		return false, fmt.Errorf("%w: unable to parse JSON of load flags", ErrMySQLShellPreCheck)
	}

	if val, ok := loadFlags["updateGtidSet"]; !ok || val != "replace" {
		return false, fmt.Errorf("%w: mysql-shell needs to restore with updateGtidSet set to \"replace\" to work with Vitess", ErrMySQLShellPreCheck)
	}

	if val, ok := loadFlags["progressFile"]; !ok || val != "" {
		return false, fmt.Errorf("%w: \"progressFile\" needs to be empty as vitess always starts a restore from scratch", ErrMySQLShellPreCheck)
	}

	if val, ok := loadFlags["skipBinlog"]; !ok || val != true {
		return false, fmt.Errorf("%w: \"skipBinlog\" needs to set to true", ErrMySQLShellPreCheck)
	}

	if val, ok := loadFlags["loadUsers"]; ok && val == true {
		shouldDeleteUsers = true
	}

	if mysqlShellSpeedUpRestore {
		version, err := params.Mysqld.GetVersionString(ctx)
		if err != nil {
			return false, fmt.Errorf("%w: failed to fetch MySQL version: %v", ErrMySQLShellPreCheck, err)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Set "updateGtidSet":"replace" in --mysql-shell-load-flags.
  2. Re-validate the full required flag set: updateGtidSet=replace and progressFile empty.
  3. After fixing, re-run the restore; the precheck aborts before any data is written so no cleanup is needed.

Example fix

// before
--mysql-shell-load-flags "{\"updateGtidSet\":\"append\",\"progressFile\":\"\"}"
// after
--mysql-shell-load-flags "{\"updateGtidSet\":\"replace\",\"progressFile\":\"\"}"
Defensive patterns

Strategy: validation

Validate before calling

flags := map[string]any{}
if err := json.Unmarshal([]byte(mysqlShellLoadFlags), &flags); err != nil {
	return err
}
if v, ok := flags["updateGtidSet"]; !ok || v != "replace" {
	return errors.Errorf("updateGtidSet must be \"replace\" for Vitess restore, got %v", v)
}

Try / catch

err := engine.ExecuteRestore(ctx, restoreParams)
if strings.Contains(err.Error(), "updateGtidSet") {
	// correct --mysql-shell-load-flags to include "updateGtidSet":"replace"
	return err
}

Prevention

When it happens

Trigger: ExecuteRestore -> restorePreCheck where the parsed --mysql-shell-load-flags JSON is missing "updateGtidSet" or has a value other than "replace" (e.g. "append" or "disable").

Common situations: Operators reusing load flags copied from vanilla MySQL Shell documentation/examples which default to a different updateGtidSet behavior; GTID replication setup on the tablet then fails or is inconsistent after restore.

Related errors


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