benbjohnson/litestream · error

list WAL segments

Error message

list WAL segments: %w

What it means

After selecting a snapshot, RestoreV3 fetches the WAL segment list for the snapshot's generation via client.WALSegmentsV3(ctx, snapshot.Generation); any backend error is wrapped as 'list WAL segments: %w'. WAL segments are needed to roll the snapshot forward to the latest transaction or requested timestamp.

Solutions

  1. Inspect the wrapped cause for the specific storage error (auth, 404, timeout) and fix it
  2. Verify the WAL directory/prefix still exists in the snapshot's generation in the replica storage
  3. Check bucket lifecycle policies are not deleting WAL segments prematurely
  4. Retry the restore; listing is read-only and safe to repeat
Defensive patterns

Strategy: retry

Validate before calling

// preflight: WAL prefix must exist for the generation you expect to restore from
if _, err := client.WALSegmentsV3(ctx, generation); err != nil {
    return fmt.Errorf("WAL listing failed: %w", err)
}

Try / catch

if err := replica.Restore(ctx, opt); err != nil {
    if strings.Contains(err.Error(), "list WAL segments") {
        log.Printf("WAL listing failed, cause=%v", errors.Unwrap(err))
        return retryWithBackoff(ctx, 3, func() error { return replica.Restore(ctx, opt) })
    }
    return err
}

Prevention

When it happens

Trigger: Calling Replica.Restore when listing WAL objects fails — backend unreachable, wrong credentials, missing/g deleted WAL prefix for the selected generation, throttled list requests, or the storage layout is not v0.3.x compatible.

Common situations: WAL retention/lifecycle rules in the bucket deleted the WAL prefix while snapshots remain; S3 permissions differing between snapshot and WAL prefixes; network outage mid-restore; pointing at a bucket written by a different litestream version with an unexpected layout.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at replica.go:1126

	// Sort all snapshots by CreatedAt for timestamp-based selection.
	sortSnapshotsV3ByCreatedAt(allSnapshots)

	// Find best snapshot across all generations (latest, or before timestamp if specified).
	snapshot := findBestSnapshotV3(allSnapshots, opt.Timestamp)
	if snapshot == nil {
		return ErrNoSnapshots
	}

	r.Logger().Debug("selected v0.3.x snapshot",
		"generation", snapshot.Generation,
		"index", snapshot.Index,
		"created_at", snapshot.CreatedAt)

	// Get WAL segments for the snapshot's generation.
	segments, err := client.WALSegmentsV3(ctx, snapshot.Generation)
	if err != nil {
		return fmt.Errorf("list WAL segments: %w", err)
	}
	segments = filterWALSegmentsV3(segments, snapshot.Index, opt.Timestamp)

	r.Logger().Debug("found v0.3.x WAL segments", "n", len(segments))

	// Create parent directory if it doesn't exist.
	var dirInfo os.FileInfo
	if db := r.DB(); db != nil {
		dirInfo = db.DirInfo()
	}
	if err := internal.MkdirAll(filepath.Dir(opt.OutputPath), dirInfo); err != nil {
		return fmt.Errorf("create parent directory: %w", err)
	}

	// Create temp file for restore.
	tmpPath := opt.OutputPath + ".tmp"
	defer func() { _ = os.Remove(tmpPath) }()

View on GitHub (pinned to 4ed7a308f6)