rqlite/rqlite · error

failed to move temporary snapshot directory %s to %s: %s

Error message

failed to move temporary snapshot directory %s to %s: %s

What it means

Upgrade7To8 wraps os.Rename failing when atomically moving the completed temporary upgrade directory into its final location. All data migration succeeded; only the final atomic swap failed. The old v7 directory is intentionally left in place so the upgrade can be retried.

Source

Thrown at snapshot/upgrader.go:162

					newSqlitePath, err)
			}
			if !db.IsValidSQLiteFile(newSqlitePath) {
				return fmt.Errorf("migrated SQLite file %s is not valid", newSqlitePath)
			}
		}

		// Ensure database file exists and convert to WAL mode.
		if err := db.EnsureWALMode(newSqlitePath); err != nil {
			return fmt.Errorf("failed to convert migrated SQLite file %s to WAL mode: %s", newSqlitePath, err)
		}
		return nil
	}(); err != nil {
		return err
	}

	// Move the upgraded snapshot directory into place.
	if err := os.Rename(newTmpDir, new); err != nil {
		return fmt.Errorf("failed to move temporary snapshot directory %s to %s: %s", newTmpDir, new, err)
	}
	if err := fsutil.SyncDirParentMaybe(new); err != nil {
		return fmt.Errorf("failed to sync parent directory of new snapshot directory %s: %s", new, err)
	}

	// We're done! Remove old.
	if err := fsutil.RemoveDirSync(old); err != nil {
		return fmt.Errorf("failed to remove old snapshot directory %s: %s", old, err)
	}
	logger.Printf("upgraded v7 snapshot directory %s to %s", old, new)
	stats.Add(upgradeOk, 1)

	return nil
}

// Upgrade8To10 writes a copy of the 8.x-format Snapshot directory at 'old' to a
// 10.x-format Snapshot directory at 'new'. In v8 format, the SQLite database file
// is stored at the root of the snapshot directory as '<id>.db', alongside a snapshot

View on GitHub (pinned to 7586a4d1bd)

Solutions

  1. Check whether the 'new' snapshots directory already exists and is non-empty; if it is a leftover from a crashed upgrade and is incomplete, remove it and restart (the old v7 dir is still intact)
  2. Ensure the parent of 'new' is writable by the rqlited user (ls -ld, chown)
  3. Ensure the snapshot store's temp dir and target dir are on the same filesystem
  4. Restart rqlited to retry the idempotent upgrade

Example fix

// before: leftover dir from a crashed upgrade blocks rename
$ ls data/snapshots  # new/ already exists with junk
// after
$ rm -rf data/snapshots/new && systemctl restart rqlited  # old v7 dir still present, upgrade retried
Defensive patterns

Strategy: validation

Validate before calling

// pre-flight: target must not exist, parent must be writable, same filesystem
if _, err := os.Stat(new); err == nil {
    return fmt.Errorf("target snapshot dir %s already exists; clean up prior failed upgrade first", new)
}
if unix.Access(filepath.Dir(new), unix.W_OK) != nil {
    return fmt.Errorf("parent of %s not writable", new)
}

Try / catch

if err := snapshot.Upgrade7To8(old, new, logger); err != nil {
    if strings.Contains(err.Error(), "failed to move temporary snapshot directory") {
        // old v7 dir still intact; resolve EXDEV/EEXIST then retry safely
    }
}

Prevention

When it happens

Trigger: Calling Upgrade7To8 when rename(newTmpDir, new) fails — target path already exists and is a non-empty directory (EXDEV aside, rename of a directory onto an existing non-empty dir fails), newTmpDir and new on different filesystems (EXDEV), or permission problems on the parent of 'new'.

Common situations: A previous crashed upgrade left the 'new' directory partially populated; data directory split across mounts; rqlited restarted over a directory with wrong ownership; container volume setups with different filesystems for tmp vs data.

Related errors


AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03). Data as JSON: /api/errors/9738c09b27dc508e. Report an issue: GitHub.