{"record":{"id":"6bf793c153de06cb","repo":"vitessio/vitess","slug":"could-not-back-up-existing-v-v","errorCode":null,"errorMessage":"could not back up existing %v: %v","messagePattern":"could not back up existing (.+?): (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/mysqlctl/mysqld.go","lineNumber":1596,"sourceCode":"\n\texisting, err := os.ReadFile(cnf.Path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not read existing file %v: %v\", cnf.Path, err)\n\t}\n\tupdated, err := os.ReadFile(f.Name())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not read updated file %v: %v\", f.Name(), err)\n\t}\n\n\tif bytes.Equal(existing, updated) {\n\t\tlog.Info(\"No changes to my.cnf. Continuing.\")\n\t\treturn nil\n\t}\n\n\tbackupPath := cnf.Path + \".previous\"\n\terr = os.Rename(cnf.Path, backupPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not back up existing %v: %v\", cnf.Path, err)\n\t}\n\terr = os.Rename(f.Name(), cnf.Path)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not move %v to %v: %v\", f.Name(), cnf.Path, err)\n\t}\n\tlog.Info(fmt.Sprintf(\"Updated my.cnf. Backup of previous version available in %v\", backupPath))\n\n\treturn nil\n}\n\n// ReinitConfig updates the config file as if Mysqld is initializing. At the\n// moment it only randomizes ServerID because it's not safe to restore a replica\n// from a backup and then give it the same ServerID as before, MySQL can then\n// skip transactions in the replication stream with the same server_id.\nfunc (mysqld *Mysqld) ReinitConfig(ctx context.Context, cnf *Mycnf) error {\n\tlog.Info(\"Mysqld.ReinitConfig\")\n\n\t// Execute as remote action on mysqlctld if requested.","sourceCodeStart":1578,"sourceCodeEnd":1614,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/mysqlctl/mysqld.go#L1578-L1614","documentation":"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.","triggerScenarios":"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.","commonSituations":"my.cnf deleted between the read and rename (TOCTOU); running refresh as different user than tablet init; directory mounted read-only.","solutions":["Check that cnf.Path still exists and the containing directory is writable.","Fix ownership of my.cnf and the directory (chown to the vitess user).","Remove a stale my.cnf.previous if it is read-only and blocking the rename.","Re-run RefreshConfig after resolving permissions."],"exampleFix":"// before\n-r--r--r-- my.cnf (owned by root), dir not writable\n// after\nchown vitess:vitess my.cnf my.cnf.previous 2>/dev/null; chmod u+w . my.cnf","handlingStrategy":"validation","validationCode":"dir := path.Dir(cnf.Path)\nif info, err := os.Stat(dir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"dir %s not usable for rename: %w\", dir, err)\n}\nif unix.Access(dir, unix.W_OK) != nil {\n    return fmt.Errorf(\"dir %s not writable\", dir)\n}","typeGuard":null,"tryCatchPattern":"if err := mysqld.RefreshConfig(ctx, cnf); err != nil {\n    if strings.Contains(err.Error(), \"could not back up existing\") {\n        log.Errorf(\"fix ownership/permissions on %s before refreshing: %v\", cnf.Path, err)\n    }\n    return err\n}","preventionTips":["Run refresh and tablet init as the same OS user","Remove stale read-only my.cnf.previous files proactively","Keep config dirs on a writable local filesystem","Check TOCTOU: avoid external removal of my.cnf during refresh"],"tags":["filesystem","rename","permissions"],"backgroundTag":"file-rename-failed","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}