benbjohnson/litestream · error
download snapshot: %w
Error message
download snapshot: %w
What it means
RestoreV3 downloads and decompresses the selected v0.3.x snapshot into a temporary file via r.downloadSnapshotV3; any failure (network read, storage get, decompression, checksum) is wrapped as 'download snapshot: %w'. The temp file is removed on failure so no partial output remains at OutputPath.
Source
Thrown at replica.go:1147
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) }()
// Download and decompress snapshot.
if err := r.downloadSnapshotV3(ctx, client, snapshot.Generation, snapshot.Index, tmpPath); err != nil {
return fmt.Errorf("download snapshot: %w", err)
}
// Apply WAL segments.
if err := r.applyWALSegmentsV3(ctx, client, snapshot.Generation, snapshot.Index, segments, tmpPath); err != nil {
return fmt.Errorf("apply WAL segments: %w", err)
}
// Rename to final path.
if err := os.Rename(tmpPath, opt.OutputPath); err != nil {
return fmt.Errorf("rename to output path: %w", err)
}
if err := internal.FsyncDir(filepath.Dir(opt.OutputPath)); err != nil {
return fmt.Errorf("sync restore output dir: %w", err)
}
if opt.IntegrityCheck != IntegrityCheckNone {
if err := checkIntegrity(ctx, opt.OutputPath, opt.IntegrityCheck); err != nil {
if ctx.Err() == nil {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Inspect the wrapped cause to distinguish network error vs decompression/corruption error
- Retry the restore with a longer timeout / more stable connection; downloads are idempotent (temp file is discarded)
- Verify the snapshot object's integrity in the backend; if corrupted, restore from an earlier snapshot via RestoreOptions.Timestamp
- Increase client timeouts or use a replica client closer to the storage region
Example fix
// before ctx := context.Background() err := replica.Restore(ctx, opt) // times out on big snapshot // after ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute) defer cancel() err := replica.Restore(ctx, opt)
Defensive patterns
Strategy: retry
Validate before calling
// preflight: HEAD the snapshot object to ensure it is downloadable and sized as expected
if _, err := client.SnapshotsV3(ctx, generation); err != nil {
return fmt.Errorf("snapshot not reachable: %w", err)
} Try / catch
if err := replica.Restore(ctx, opt); err != nil {
if strings.Contains(err.Error(), "download snapshot") {
log.Printf("snapshot download failed, cause=%v", errors.Unwrap(err))
return retryWithBackoff(ctx, 3, func() error { return replica.Restore(ctx, opt) })
}
return err
} Prevention
- Use a context timeout sized for the largest expected snapshot, not the default
- Run restores from a host with reliable bandwidth to the storage region
- Periodically verify snapshot integrity in the replica (restore drills)
- Handle context cancellation explicitly so partial downloads are retried, not mistaken for corruption
When it happens
Trigger: Calling Replica.Restore when fetching the snapshot object from the replica client fails: backend read error mid-stream, context cancellation/timeout during a large download, corrupted snapshot data that fails decompression, or the snapshot object was deleted between listing and download.
Common situations: Large databases and short HTTP timeouts/proxy idle cutoffs; flaky VPN/network during DR; S3 throttling on big GETs; corrupted snapshot object in storage; restore interrupted by ctx cancellation from the caller.
Related errors
- list generations: %w
- list snapshots for generation %s: %w
- list WAL segments: %w
- apply WAL segments: %w
- write WAL segment %d/%d: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/a07c39510a7761e3.
Report an issue: GitHub.