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
- Set "updateGtidSet":"replace" in --mysql-shell-load-flags.
- Re-validate the full required flag set: updateGtidSet=replace and progressFile empty.
- 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
- Pin the required load-flag pair (updateGtidSet=replace, empty progressFile) in your config template.
- Do not copy util.loadDump examples from MySQL Shell docs verbatim into Vitess flags.
- Add a CI config check asserting updateGtidSet=="replace" when the mysqlshell engine is enabled.
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
- %w: unable to parse JSON of load flags
- %w: no backup location set via --mysql-shell-backup-location
- %w: at least the --js flag is required in the value of the f
- ErrMySQLShellPreCheck
- GetPreviousGTIDs: previous GTIDs not found
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/758bf8ce4a332b2a.
Report an issue: GitHub.