benbjohnson/litestream · critical

restore from backup: %w

Error message

restore from backup: %w

What it means

DB.EnsureExists(ctx) attempts to restore the database from the replica via db.Replica.Restore(ctx, opt). If the restore fails with anything other than ErrTxNotAvailable or ErrNoSnapshots (which mean "no backup yet" and are treated as fresh-start), litestream wraps the error as "restore from backup". The database is NOT usable until this is resolved.

Source

Thrown at db.go:762

		return fmt.Errorf("stat database: %w", err)
	}

	if dir := filepath.Dir(db.Path()); dir != "." {
		if err := os.MkdirAll(dir, 0o750); err != nil {
			return fmt.Errorf("create parent directory: %w", err)
		}
	}

	opt := NewRestoreOptions()
	opt.OutputPath = db.Path()
	opt.IntegrityCheck = IntegrityCheckQuick

	if err := db.Replica.Restore(ctx, opt); err != nil {
		if errors.Is(err, ErrTxNotAvailable) || errors.Is(err, ErrNoSnapshots) {
			db.Logger.Debug("no backup found, will create fresh database")
			return nil
		}
		return fmt.Errorf("restore from backup: %w", err)
	}

	db.Logger.Info("database restored from backup", "path", db.Path())
	return nil
}

// Open initializes the background monitoring goroutine.
func (db *DB) Open() (err error) {
	db.mu.Lock()
	if db.opened {
		db.mu.Unlock()
		return nil // already open
	}
	// Recreate context for fresh start (handles reopen after close)
	db.ctx, db.cancel = context.WithCancel(context.Background())
	db.mu.Unlock()

	// Validate fields on database.

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Unwrap the error to see whether the failure was remote fetch, write, or integrity check.
  2. Inspect the replica contents with `litestream ltx -level all` to verify the LTX chain and snapshots are intact.
  3. Verify read (GetObject) permissions and network access to the replica destination, then retry the restore.
  4. If remote state is corrupted/unusable and you accept data loss, remove/renaming the replica path and start fresh (a new DB will be created on Open()) — otherwise repair from another replica copy.

Example fix

// before
if err := db.EnsureExists(ctx); err != nil {
    log.Fatal(err) // opaque "restore from backup: ..."
}
// after
if err := db.EnsureExists(ctx); err != nil {
    if errors.Is(err, litestream.ErrTxNotAvailable) {
        // no backup yet: fresh DB will be created
    } else {
        log.Fatalf("restore failed: %v", err) // inspect wrapped cause
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: read access to the replica before attempting restore
// e.g. run `litestream ltx -level all` against the replica and fail fast if unreadable

Try / catch

if err := db.EnsureExists(ctx); err != nil {
    switch {
    case errors.Is(err, litestream.ErrTxNotAvailable), errors.Is(err, litestream.ErrNoSnapshots):
        // treat as fresh start
    default:
        return fmt.Errorf("restore from backup failed: %w", err)
    }
}

Prevention

When it happens

Trigger: Replica.Restore failing during the local restore: remote LTX fetch failures (network/credentials), integrity check failure (opt.IntegrityCheck = IntegrityCheckQuick), corrupted remote LTX files, or unsupported/failed output write to db.Path().

Common situations: Remote replica truncated or corrupted by lifecycle rules; network interruption mid-restore; restore target path unwritable; quick integrity check failing because the latest LTX chain is inconsistent; provider credentials valid for listing but not for GetObject.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/691404bd4ae05041. Report an issue: GitHub.