benbjohnson/litestream · error
list level %d ltx files: %w
Error message
list level %d ltx files: %w
What it means
fillFollowGap, which tries to bridge a TXID gap in level-0 files by scanning compaction levels 1..SnapshotLevel-1, failed while listing the LTX files for a level via the storage client (r.Client.LTXFiles). The wrapped error comes from the storage backend, not from local files.
Source
Thrown at replica.go:1010
}
}
if err := dec.Close(); err != nil {
return fmt.Errorf("close decoder: %w", err)
}
return f.Sync()
}
// fillFollowGap attempts to bridge a gap in level 0 files by searching
// higher compaction levels for a file that covers the missing TXID range.
func (r *Replica) fillFollowGap(ctx context.Context, f *os.File, afterTXID ltx.TXID, gapMinTXID ltx.TXID, pageSize uint32) (ltx.TXID, error) {
currentTXID := afterTXID
for level := 1; level < SnapshotLevel; level++ {
itr, err := r.Client.LTXFiles(ctx, level, 0, false)
if err != nil {
return currentTXID, fmt.Errorf("list level %d ltx files: %w", level, err)
}
closeLevel := func(retErr error) (ltx.TXID, error) {
if closeErr := itr.Close(); closeErr != nil {
closeErr = fmt.Errorf("close level %d ltx iterator: %w", level, closeErr)
if retErr != nil {
return currentTXID, errors.Join(retErr, closeErr)
}
return currentTXID, closeErr
}
return currentTXID, retErr
}
for itr.Next() {
info := itr.Item()
// Skip if there's a gap at this level too.
if info.MinTXID > currentTXID+1 {
breakView on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify storage credentials and permissions for listing objects in the bucket/container
- Check network connectivity and proxy settings to the storage endpoint
- Confirm bucket/region/container configuration in the litestream config file
- Check for storage-side rate limits (429s) and add backoff/reduce polling frequency
Example fix
// before: missing s3:ListBucket permission
// policy lacks s3:ListBucket on the bucket
// after: grant it
{ "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"], "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"] } Defensive patterns
Strategy: retry
Validate before calling
// preflight: can we list the replica path?
itr, err := client.LTXFiles(ctx, 1, 0, false)
if err != nil {
return fmt.Errorf("storage preflight failed (check credentials/bucket): %w", err)
}
itr.Close() Try / catch
if err := r.applyNewLTXFiles(ctx, f, pageSize); err != nil {
var storageErr *StorageError
if errors.As(err, &storageErr) || strings.Contains(err.Error(), "list level") {
// transient listing failure: retry with backoff
return retryWithBackoff(3, func() error { return r.applyNewLTXFiles(ctx, f, pageSize) })
}
return err
} Prevention
- Grant s3:ListBucket (or equivalent) on the replica bucket/prefix
- Rotate credentials before expiry; use instance roles where possible
- Set explicit region/endpoint in the litestream config
- Watch for 429 throttling and back off on list-heavy follow loops
When it happens
Trigger: r.Client.LTXFiles(ctx, level, 0, false) returns an error for levels 1-3: storage API failure listing objects, expired/invalid credentials, network failure, or a misconfigured bucket/container.
Common situations: S3/GCS/Azure credential expiry or permission loss, rate limiting on ListObjects, region or bucket name misconfiguration, network outage during replica follow mode.
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
- replica sync: %w
- fetch ltx files: %w
- cannot determine L%d max ltx file for %q: %w
- write ltx file: %w
- close level %d ltx iterator: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/3fb280f7419d43e3.
Report an issue: GitHub.