benbjohnson/litestream · error
iterate level %d ltx files: %w
Error message
iterate level %d ltx files: %w
What it means
After iterating a level's LTX files, fillFollowGap checks itr.Err() for any deferred iteration error (errors that surface during Next() rather than being returned immediately). If set, it is wrapped with the level number and returned; the iterator is still closed and any close error is joined.
Source
Thrown at replica.go:1051
continue
}
if err := r.applyLTXFile(ctx, f, info, pageSize); err != nil {
return closeLevel(fmt.Errorf(
"apply gap-fill ltx file (level=%d, min=%s, max=%s): %w",
info.Level, info.MinTXID, info.MaxTXID, err,
))
}
currentTXID = info.MaxTXID
// If we've bridged past the gap, we're done.
if currentTXID+1 >= gapMinTXID {
return closeLevel(nil)
}
}
if iterErr := itr.Err(); iterErr != nil {
return closeLevel(fmt.Errorf("iterate level %d ltx files: %w", level, iterErr))
}
if _, err := closeLevel(nil); err != nil {
return currentTXID, err
}
// If we made progress at this level, restart from level 1.
if currentTXID > afterTXID {
return currentTXID, nil
}
}
return currentTXID, nil
}
// RestoreV3 restores from a v0.3.x format backup.
func (r *Replica) RestoreV3(ctx context.Context, opt RestoreOptions) error {
client, ok := r.Client.(ReplicaClientV3)
if !ok {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Check the wrapped itr.Err() cause for storage-side issues (403/429/timeouts) and address it
- Verify IAM policy allows listing across all pages/prefixes of the replica path
- Retry the sync; listing errors are typically transient
- If using a custom ReplicaClient, ensure Next() propagates errors to Err() correctly
Example fix
// before: custom iterator swallowing errors mid-listing
func (i *Iter) Next() bool { if !i.next() { return false }; return true } // error lost
// after
func (i *Iter) Next() bool {
if !i.next() { i.err = i.fetchErr(); return false }
return true
} Defensive patterns
Strategy: retry
Validate before calling
// preflight full listing before relying on incremental follow
itr, err := client.LTXFiles(ctx, 2, 0, false)
if err != nil { return err }
for itr.Next() { _ = itr.Item() }
if err := itr.Err(); err != nil { return fmt.Errorf("listing not fully traversable: %w", err) }
return itr.Close() Try / catch
if err := r.applyNewLTXFiles(ctx, f, pageSize); err != nil {
if strings.Contains(err.Error(), "iterate level") {
return retryWithBackoff(3, func() error { return r.applyNewLTXFiles(ctx, f, pageSize) })
}
return err
} Prevention
- Ensure IAM allows listing all pages/prefixes of the replica path
- Handle 429s with exponential backoff in client configurations
- Implement custom iterators so Next() failures always surface via Err()
- Keep follow sessions short or re-establish periodically to reset listing state
When it happens
Trigger: The storage iterator hit an error mid-iteration (e.g., a paginated S3 listing failed on a later page, an object metadata read failed), causing itr.Next() to return false and itr.Err() to be non-nil for levels 1 through SnapshotLevel-1.
Common situations: Long-lived follow sessions where a multi-page listing fails partway (token expired, 403 on later page, throttling), inconsistent storage backends with eventual-consistency listing gaps, custom client returning errors from Next without aborting.
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/70eb47f8bf63fe9a.
Report an issue: GitHub.