vitessio/vitess · error

running MySQL version %q is newer than backup MySQL version

Error message

running MySQL version %q is newer than backup MySQL version %q which is not safe to upgrade

What it means

Thrown by the backup restore version-compatibility check (checkRestoreVersion) in backupengine.go when the running MySQL version is newer than the version a backup was taken with, the versions are in different LTS release streams, and the backup is not marked upgrade-safe. Vitess refuses to restore such a backup because restoring an upgrade-unsafe backup onto a newer MySQL version can corrupt the data dictionary / system tables.

Source

Thrown at go/vt/mysqlctl/backupengine.go:690

	}

	if flavorTo != flavorFrom {
		return fmt.Errorf("cannot use backup between different flavors: %q vs. %q", from, to)
	}

	if parsedTo == parsedFrom {
		return nil
	}

	// If we're not on the same LTS stream, we have to do additional checks to see if it's safe to
	// to upgrade. It can only be one newer LTS version for the destination and the backup
	// has to be marked as upgrade safe.

	// If something is across different LTS streams and not upgrade safe, we can't use it.
	if !parsedFrom.isSameRelease(parsedTo) {
		if !upgradeSafe {
			if parsedTo.atLeast(parsedFrom) {
				return fmt.Errorf("running MySQL version %q is newer than backup MySQL version %q which is not safe to upgrade", to, from)
			}
			return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
		}

		// Alright, we're across different LTS streams and the backup is upgrade safe.
		// We can only upgrade to the next LTS version.
		for i, ltsVersion := range ltsVersions {
			if parsedFrom.isSameRelease(ltsVersion) {
				if i < len(ltsVersions)-1 && parsedTo.isSameRelease(ltsVersions[i+1]) {
					return nil
				}
				if parsedTo.atLeast(parsedFrom) {
					return fmt.Errorf("running MySQL version %q is too new for backup MySQL version %q", to, from)
				}
				return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
			}
		}
		if parsedTo.atLeast(parsedFrom) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Take a fresh backup with the currently running (newer) MySQL version and restore from that backup instead.
  2. Downgrade the tablet's mysqld back to the same release stream the backup was taken with, then restore.
  3. Re-take the backup using a MySQL version that is upgrade-safe for the target version per MySQL's in-place upgrade rules, then retry the restore.
  4. Verify the backup manifest's MySQLVersion was recorded correctly so upgradeSafe evaluation is accurate.

Example fix

// before: restoring an 8.0.x backup onto an 8.4.x tablet fails
mysqlctl restore ...
// after: take a new backup with 8.4.3 running first, then restore
mysqlctl backup  # with mysqld 8.4.3
mysqlctl restore  # succeeds from the new backup
Defensive patterns

Strategy: validation

Validate before calling

backupVer := parseVersion(backupManifest.MySQLVersion)
runningVer := parseVersion(getRunningMySQLVersion())
if runningVer.atLeast(backupVer) && !backupVer.isSameRelease(runningVer) {
    return errors.New("backup not restorable on newer MySQL: retake backup or use upgrade-safe backup")
}

Prevention

When it happens

Trigger: Restore (BackupEngine.Restore / mysqlctl restore) where parsedTo.atLeast(parsedFrom) is true, !parsedFrom.isSameRelease(parsedTo) (different release streams, e.g. 8.0.x backup on 8.4.y tablet), and the backup manifest lacks the upgradeSafe marker.

Common situations: Operator upgraded a tablet's MySQL binary (8.0 -> 8.4) but existing backups were taken before the upgrade and are not upgrade-safe; restoring an old backup onto a newly provisioned, newer MySQL tablet.

Related errors


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