rqlite/rqlite · error

pre-backup snapshot failed: %s

Error message

pre-backup snapshot failed: %s

What it means

During a backup operation, the Store takes a pre-backup snapshot (via s.Snapshot(0)) to ensure the Raft log is truncated and the SQLite file is in a stable state before it is copied. If the snapshot fails with an error that is not ErrNothingNewToSnapshot or the known 'wait until the configuration entry at...' condition, the backup aborts with 'pre-backup snapshot failed: %s'.

Source

Thrown at store/store.go:1817

			defer os.Remove(srcFD.Name())
			defer srcFD.Close()
			if err := s.db.Backup(srcFD.Name(), br.Vacuum); err != nil {
				return err
			}
		} else {
			// If there is data in the WAL we need to do a snapshot to ensure that the
			// backup we take of the main database file is up-to-date. Doing this check
			// is an optimization, it's not crticial that it is 100% correct as we still
			// check for the "nothing new to snapshot" error anyway.
			sz, err := s.db.WALSize()
			if err != nil {
				return err
			}
			if sz > 0 {
				if err := s.Snapshot(0); err != nil {
					if !errors.Is(err, ErrNothingNewToSnapshot) &&
						!strings.Contains(err.Error(), "wait until the configuration entry at") {
						return fmt.Errorf("pre-backup snapshot failed: %s", err.Error())
					}
				}
			}
			// Block any snapshotting which will allow us to read the SQLite file without
			// it changing underneath us. Any incoming writes will be sent to the WAL, so
			// write traffic is not blocked during the backup process.
			if err := s.snapshotCAS.BeginWithRetry("backup", backupCASTimeout, backupCASRetryDelay); err != nil {
				return ErrBackupCASFailed
			}
			defer s.snapshotCAS.End()

			// Now we can copy the SQLite file directly.
			srcFD, err = os.Open(s.dbPath)
			if err != nil {
				return fmt.Errorf("failed to open database file: %s", err.Error())
			}
			defer srcFD.Close()
		}

View on GitHub (pinned to 7586a4d1bd)

Solutions

  1. Retry the backup after the transient Raft condition clears (election/snapshot completes).
  2. Verify the node is the leader and Raft is healthy before triggering backups.
  3. Check disk space and permissions on the snapshot directory.
  4. If the cause is raft log compaction contention, increase -raft-snap thresholds or schedule backups off-peak.

Example fix

// before
status := 500; msg = fmt.Sprintf("pre-backup snapshot failed: %s", err)
// after (caller-side)
if strings.Contains(err, "pre-backup snapshot failed") {
    time.Sleep(2 * time.Second)
    retryBackup()
}
Defensive patterns

Strategy: retry

Validate before calling

st := getNodeStatus(nodeURL)
if st["store"]["raft"]["state"] != "leader" || !st["store"]["raft"]["applied_index"]... {
    // trigger backup only on healthy leader
}

Type guard

func isPreBackupSnapshotErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "pre-backup snapshot failed")
}

Try / catch

err := runBackup(nodeURL)
if isPreBackupSnapshotErr(err) {
    time.Sleep(5 * time.Second)
    err = runBackup(nodeURL) // one retry after Raft settles
}

Prevention

When it happens

Trigger: Calling the /db/backup endpoint (Store.Backup) when the underlying raft.Snapshot() call fails for reasons other than 'nothing new to snapshot': Raft in a bad state, loss of leadership mid-operation, snapshot in progress, or I/O errors writing the snapshot.

Common situations: Automated backup cron jobs hitting /db/backup while the node is not leader or is mid-snapshot; disk full preventing snapshot writes; cluster instability during backup windows; large backlogs of unsnapshotted entries combined with I/O issues.

Related errors


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