vitessio/vitess · error

could not back up existing %v: %v

Error message

could not back up existing %v: %v

What it means

RefreshConfig detected a config change and tried to back up the current my.cnf by renaming it to my.cnf.previous, but os.Rename failed. Wrapped from the OS; usually a permissions or cross-filesystem issue.

Source

Thrown at go/vt/mysqlctl/mysqld.go:1596

	existing, err := os.ReadFile(cnf.Path)
	if err != nil {
		return fmt.Errorf("could not read existing file %v: %v", cnf.Path, err)
	}
	updated, err := os.ReadFile(f.Name())
	if err != nil {
		return fmt.Errorf("could not read updated file %v: %v", f.Name(), err)
	}

	if bytes.Equal(existing, updated) {
		log.Info("No changes to my.cnf. Continuing.")
		return nil
	}

	backupPath := cnf.Path + ".previous"
	err = os.Rename(cnf.Path, backupPath)
	if err != nil {
		return fmt.Errorf("could not back up existing %v: %v", cnf.Path, err)
	}
	err = os.Rename(f.Name(), cnf.Path)
	if err != nil {
		return fmt.Errorf("could not move %v to %v: %v", f.Name(), cnf.Path, err)
	}
	log.Info(fmt.Sprintf("Updated my.cnf. Backup of previous version available in %v", backupPath))

	return nil
}

// ReinitConfig updates the config file as if Mysqld is initializing. At the
// moment it only randomizes ServerID because it's not safe to restore a replica
// from a backup and then give it the same ServerID as before, MySQL can then
// skip transactions in the replication stream with the same server_id.
func (mysqld *Mysqld) ReinitConfig(ctx context.Context, cnf *Mycnf) error {
	log.Info("Mysqld.ReinitConfig")

	// Execute as remote action on mysqlctld if requested.

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Check that cnf.Path still exists and the containing directory is writable.
  2. Fix ownership of my.cnf and the directory (chown to the vitess user).
  3. Remove a stale my.cnf.previous if it is read-only and blocking the rename.
  4. Re-run RefreshConfig after resolving permissions.

Example fix

// before
-r--r--r-- my.cnf (owned by root), dir not writable
// after
chown vitess:vitess my.cnf my.cnf.previous 2>/dev/null; chmod u+w . my.cnf
Defensive patterns

Strategy: validation

Validate before calling

dir := path.Dir(cnf.Path)
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
    return fmt.Errorf("dir %s not usable for rename: %w", dir, err)
}
if unix.Access(dir, unix.W_OK) != nil {
    return fmt.Errorf("dir %s not writable", dir)
}

Try / catch

if err := mysqld.RefreshConfig(ctx, cnf); err != nil {
    if strings.Contains(err.Error(), "could not back up existing") {
        log.Errorf("fix ownership/permissions on %s before refreshing: %v", cnf.Path, err)
    }
    return err
}

Prevention

When it happens

Trigger: Mysqld.RefreshConfig where bytes differ: os.Rename(cnf.Path, cnf.Path+".previous") fails because cnf.Path doesn't exist, is owned by another user, or the directory is not writable.

Common situations: my.cnf deleted between the read and rename (TOCTOU); running refresh as different user than tablet init; directory mounted read-only.

Related errors


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