benbjohnson/litestream · error
remove expired l0 files: %w
Error message
remove expired l0 files: %w
What it means
Wraps a failure from DeleteLTXFiles when removing expired L0 files after retention enforcement computed the deletion list. The remote deletion failed (permissions, object lock, network); local cleanup still proceeds and remote deletion can be retried.
Source
Thrown at db.go:3124
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "eligible").Set(float64(len(deleted)))
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "not_compacted").Set(float64(notCompactedCount))
internal.L0RetentionGaugeVec.WithLabelValues(dbName, "too_recent").Set(float64(tooRecentCount))
db.Logger.Debug("l0 retention scan complete",
"total_l0_files", totalFiles,
"eligible_for_deletion", len(deleted),
"not_compacted_yet", notCompactedCount,
"too_recent", tooRecentCount,
"max_l1_txid", maxL1TXID)
if len(deleted) == 0 {
return nil
}
if !db.RetentionEnabled {
db.Logger.Debug("skipping remote deletion (retention disabled)", "level", 0, "count", len(deleted))
} else if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil {
return fmt.Errorf("remove expired l0 files: %w", err)
}
for _, info := range deleted {
localPath := db.LTXPath(0, info.MinTXID, info.MaxTXID)
db.Logger.Debug("deleting expired local l0 file", "minTXID", info.MinTXID, "maxTXID", info.MaxTXID, "path", localPath)
if err := os.Remove(localPath); err != nil && !os.IsNotExist(err) {
db.Logger.Error("failed to remove local l0 file", "path", localPath, "error", err)
}
}
if len(deleted) > 0 {
db.invalidatePosCache()
}
db.Logger.Info("l0 retention enforced", "deleted_count", len(deleted), "max_l1_txid", maxL1TXID)
return nil
}
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Grant delete permission on the replica prefix to the credentials in use.
- Check bucket policies (object lock, versioning, retention) blocking deletes.
- Retry the retention cycle; deletions are idempotent.
- Manually delete reported stuck keys if one object blocks the batch.
- Disable litestream retention only if a storage lifecycle policy handles cleanup.
Example fix
// before
if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil { return fmt.Errorf("remove expired l0 files: %w", err) }
// after
if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil {
if !isTransient(err) { return fmt.Errorf("remove expired l0 files: %w", err) } // else retry next cycle
} Defensive patterns
Strategy: try-catch
Validate before calling
// verify delete permission: aws iam simulate-principal-policy --action-names s3:DeleteObject
Try / catch
if err := db.Replica.Client.DeleteLTXFiles(ctx, deleted); err != nil {
if isTransient(err) { scheduleRetry() } else { logAndAlert(fmt.Errorf("remove expired l0 files: %w", err)) }
} Prevention
- Least-privilege policies must still include delete on the replica prefix.
- Avoid versioning/lock configs that block deletes.
- Monitor delete failures; local cleanup proceeds but remote grows.
When it happens
Trigger: L0 retention with RetentionEnabled=true deleting expired L0 files while the backend rejects or interrupts the delete: missing s3:DeleteObject permission, GCS retention policy, or a network failure mid-batch (db.go:3124).
Common situations: Least-privilege IAM policies allowing put/get but not delete; S3 versioned buckets with delete constraints; rate limits on bulk deletes.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- remove ltx files: %w
- remove ltx files: %w
- abs: cannot delete ltx file %q: %w
- abs: cannot list blobs: %w
- abs: cannot delete blob %q: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/e733c9f10a81bcf2.
Report an issue: GitHub.