vitessio/vitess · error · ErrMySQLShellPreCheck

%w: "progressFile" needs to be empty as vitess always starts

Error message

%w: "progressFile" needs to be empty as vitess always starts a restore from scratch

What it means

restorePreCheck in the MySQL Shell backup engine validates the `loadFlags` configured for mysql-shell's load dump utility. Vitess always restores from scratch, so a progress file (which enables resuming a partial load) is not supported. This error is wrapped with ErrMySQLShellPreCheck so callers can detect pre-check failures.

Source

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

}

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)
		}

		_, sv, err := ParseVersionString(version)
		if err != nil {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Remove the "progressFile" entry from loadFlags in the backupengine mysqlshell config, or set it to an empty string
  2. Review the other mandatory loadFlags: updateGtidSet must be "replace" and skipBinlog must be true
  3. If resumable loads are needed, do not use the mysql-shell engine; restore is always started from scratch under Vitess

Example fix

// before
"loadFlags": {"updateGtidSet": "replace", "progressFile": "/tmp/load.progress", "skipBinlog": true}
// after
"loadFlags": {"updateGtidSet": "replace", "progressFile": "", "skipBinlog": true}
Defensive patterns

Strategy: validation

Validate before calling

flags, _ := loadFlags["progressFile"]
if v, ok := loadFlags["progressFile"]; ok && v != "" {
    return fmt.Errorf("mysql-shell loadFlags.progressFile must be empty for Vitess restores, got %v", v)
}

Try / catch

// Go: detect the sentinel on restore failure
if _, err := engine.ExecuteRestore(ctx, params, backupDir); err != nil {
    if errors.Is(err, mysqlctl.ErrMySQLShellPreCheck) {
        // fix loadFlags in backupengine config before retrying
    }
}

Prevention

When it happens

Trigger: Calling MySQLShellBackupEngine.ExecuteRestore (via restorePreCheck) when the backupengine's `loadFlags` map contains "progressFile" with any non-empty value, or when the key is absent and the default in the config template is not overridden to empty.

Common situations: Operators copy a mysql-shell load command example that uses `--progress-file=load.progress` for resumable loads into the Vitess `loadFlags` setting; existing flags from a non-Vitess mysql-shell workflow are reused without removing progressFile.

Related errors


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