benbjohnson/litestream · error
open ltx file: %w
Error message
open ltx file: %w
What it means
Hydrator.Restore builds the hydration file by opening each LTX file listed in `infos` from the replica client and compacting them together. This error wraps a failure from client.OpenLTXFile while opening one of those files. It is a thin wrapper: the underlying cause (from the storage backend) is embedded via %w and is the real diagnostic.
Source
Thrown at vfs.go:776
}
// Restore restores the database from LTX files to the hydration file.
func (h *Hydrator) Restore(ctx context.Context, infos []*ltx.FileInfo) error {
// Open all LTX files as readers
rdrs := make([]io.Reader, 0, len(infos))
defer func() {
for _, rd := range rdrs {
if closer, ok := rd.(io.Closer); ok {
_ = closer.Close()
}
}
}()
for _, info := range infos {
h.logger.Debug("opening ltx file for hydration", "level", info.Level, "min", info.MinTXID, "max", info.MaxTXID)
rc, err := h.client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0)
if err != nil {
return fmt.Errorf("open ltx file: %w", err)
}
rdrs = append(rdrs, rc)
}
if len(rdrs) == 0 {
return fmt.Errorf("no ltx files for hydration")
}
// Compact and decode using io.Pipe pattern
pr, pw := io.Pipe()
c, err := ltx.NewCompactor(pw, rdrs)
if err != nil {
return fmt.Errorf("new ltx compactor: %w", err)
}
c.HeaderFlags = ltx.HeaderFlagNoChecksum
h.compactor = c
go func() {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Unwrap the error (errors.Unwrap / %v print) to see the backend cause; fix storage credentials, network, or bucket config accordingly.
- Re-list LTX files immediately before Restore so `infos` reflects what the replica currently holds; skip or re-fetch missing files.
- If the replica state is corrupt or files were pruned, run `litestream reset` for the database and re-hydrate from the latest snapshot.
- Verify retention settings so the files needed for restore are not deleted mid-restore; raise retention or disable lifecycle cleanup on the bucket.
Example fix
// before
infos, _ := client.ListLTXFiles(ctx)
err := hydrator.Restore(ctx, infos)
// after
infos, err := client.ListLTXFiles(ctx)
if err != nil { return err }
infos = filterExisting(ctx, client, infos) // drop pruned files before opening
err = hydrator.Restore(ctx, infos) Defensive patterns
Strategy: retry
Validate before calling
// check file presence before restore
for _, info := range infos {
if _, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, 0); err != nil {
return fmt.Errorf("ltx %d-%d unavailable: %w", info.MinTXID, info.MaxTXID, err)
}
} Try / catch
if err := hydrator.Restore(ctx, infos); err != nil {
var nf *fs.PathError
if errors.As(err, &nf) { /* re-list and fall back to snapshot restore */ }
} Prevention
- Re-list LTX files immediately before restore to avoid stale FileInfos
- Set retention longer than the maximum expected restore time
- Validate storage credentials at startup with a cheap HEAD/List call
When it happens
Trigger: Calling Hydrator.Restore(ctx, infos) where any info entry references an LTX file that OpenLTXFile cannot open on the replica — object deleted by retention/GC, wrong bucket/container credentials, network failure, or the FileInfo metadata being stale relative to what the replica actually holds.
Common situations: Replica retention removed a level-0 LTX file between the ListLTXFiles call and Restore; S3/GCS credentials rotated or revoked; restoring after `litestream reset` against a replica with mismatched TXIDs; offline development machine pointing at an unreachable object store.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- replica sync: %w
- fetch ltx files: %w
- cannot determine L%d max ltx file for %q: %w
- max retries exceeded reading ltx file (level=%d, min=%s, max
- write ltx file: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/23bdf106f4e8c672.
Report an issue: GitHub.