benbjohnson/litestream · error

find LTX snapshots

Error message

find LTX snapshots: %w

What it means

Wraps an error from FindLTXFiles when collecting snapshot-level LTX files before a target timestamp. It means the snapshot listing itself failed (as opposed to returning zero matches). The best candidate snapshot could not be determined.

Solutions

  1. Read the wrapped cause and fix the backend listing failure (auth, endpoint, network).
  2. Retry; use litestream ltx -level all to verify snapshot files are listable.
  3. Confirm the replica path in config matches the actual bucket layout.
  4. As a fallback, restore latest without a timestamp.

Example fix

// before
snapshots, err := FindLTXFiles(ctx, r.Client, SnapshotLevel, true, filter)
if err != nil {
    return nil, fmt.Errorf("find LTX snapshots: %w", err)
}
// after
// verify listing works:
// $ litestream ltx -level 4 -level 5 <db-path>
// then re-run restore
snapshots, err := FindLTXFiles(ctx, r.Client, SnapshotLevel, true, filter)
Defensive patterns

Strategy: try-catch

Try / catch

snapshots, err := findBestLTXSnapshotForTimestamp(ctx, ts)
if err != nil {
    // wrapped backend listing error: fix endpoint/auth, then retry with backoff
    return fmt.Errorf("snapshot listing failed: %w", err)
}
if snapshots == nil {
    // zero matches is NOT an error: fall back to another format
}

Prevention

When it happens

Trigger: Restore -> shouldUseV3Restore -> findBestLTXSnapshotForTimestamp: the replica client errors while listing snapshot-level files (network, auth, corrupt listing) rather than simply finding none.

Common situations: Object storage outages during DR drills; expired session tokens; misconfigured endpoint URLs after provider migration.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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

Appendix: source

Thrown at replica.go:1503

			}
			if updatedAt.IsZero() || seg.CreatedAt.After(updatedAt) {
				updatedAt = seg.CreatedAt
			}
		}
	}

	return createdAt, updatedAt, nil
}

// findBestLTXSnapshotForTimestamp returns the best LTX snapshot for the given timestamp.
// Returns nil if no suitable snapshot exists.
func (r *Replica) findBestLTXSnapshotForTimestamp(ctx context.Context, timestamp time.Time) (*ltx.FileInfo, error) {
	// Find snapshots at the snapshot level that are before the timestamp.
	snapshots, err := FindLTXFiles(ctx, r.Client, SnapshotLevel, true, func(info *ltx.FileInfo) (bool, error) {
		return info.CreatedAt.Before(timestamp), nil
	})
	if err != nil {
		return nil, fmt.Errorf("find LTX snapshots: %w", err)
	}
	if len(snapshots) == 0 {
		return nil, nil
	}

	// Return the latest snapshot before the timestamp (last in the sorted list).
	return snapshots[len(snapshots)-1], nil
}

// CalcRestorePlan returns a list of storage paths to restore a snapshot at the given TXID.
func CalcRestorePlan(ctx context.Context, client ReplicaClient, txID ltx.TXID, timestamp time.Time, logger *slog.Logger) ([]*ltx.FileInfo, error) {
	if txID != 0 && !timestamp.IsZero() {
		return nil, fmt.Errorf("cannot specify both TXID & timestamp to restore")
	}

	var infos ltx.FileInfoSlice
	logger = logger.With("target", txID)

View on GitHub (pinned to 4ed7a308f6)