benbjohnson/litestream · error
close l1 iterator: %w
Error message
close l1 iterator: %w
What it means
Wraps an error from closing the L1 file iterator after determining maxL1TXID. Iterator Close flushes pending backend errors (e.g. failed pagination of a cloud listing), so a close error means the listing may have been incomplete and the computed max TXID cannot be trusted.
Source
Thrown at db.go:3041
db.Logger.Debug("starting l0 retention enforcement", "retention", db.L0Retention)
dbName := filepath.Base(db.Path())
// Determine the highest TXID that has been compacted into L1.
itr, err := db.Replica.Client.LTXFiles(ctx, 1, 0, false)
if err != nil {
return fmt.Errorf("fetch l1 files: %w", err)
}
var maxL1TXID ltx.TXID
for itr.Next() {
info := itr.Item()
if info.MaxTXID > maxL1TXID {
maxL1TXID = info.MaxTXID
}
}
if err := itr.Close(); err != nil {
return fmt.Errorf("close l1 iterator: %w", err)
}
if maxL1TXID == 0 {
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "eligible").Set(0)
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "not_compacted").Set(0)
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "too_recent").Set(0)
return nil
}
threshold := time.Now().Add(-db.L0Retention)
itr, err = db.Replica.Client.LTXFiles(ctx, 0, 0, false)
if err != nil {
return fmt.Errorf("fetch l0 files: %w", err)
}
defer itr.Close()
var (
deleted []*ltx.FileInfo
lastInfo *ltx.FileInfoView on GitHub (pinned to 4ed7a308f6)
Solutions
- Retry the operation; a later successful Close confirms a complete listing.
- Prune old files via retention so listings stay small.
- Check provider throttling limits and request pacing for large buckets.
- Confirm custom ReplicaClient implementations propagate page-fetch errors through Close.
Example fix
// before
if err := itr.Close(); err != nil { return fmt.Errorf("close l1 iterator: %w", err) }
// after (caller retry with backoff)
if err := enforceL0Retention(ctx, db); err != nil && isTransient(err) {
time.Sleep(backoff)
err = enforceL0Retention(ctx, db)
} Defensive patterns
Strategy: retry
Try / catch
if err := itr.Close(); err != nil {
if isTransient(err) { return retryWithBackoff(ctx) }
return fmt.Errorf("close l1 iterator: %w", err)
} Prevention
- Always treat iterator Close errors as listing failures, never ignore them.
- Keep buckets pruned so listings span fewer pages.
- Watch provider throttling metrics for large buckets.
When it happens
Trigger: L0 retention run where itr.Close() returns an error from the underlying remote listing, typically a failed S3/GS continuation-token page fetch that the iterator surfaces only on Close (db.go:3041).
Common situations: Large buckets whose listing spans many pages; transient 5xx or timeout on a later page; provider throttling during long listings.
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
- close l1 iterator: %w
- fetch l1 files: %w
- fetch l0 files: %w
- fetch l0 files: %w
- oss: list objects page: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/75ba7cb3c30c0041.
Report an issue: GitHub.