{"record":{"id":"fe6d1ddf49d2dc1c","repo":"benbjohnson/litestream","slug":"remove-expired-l0-files-w","errorCode":null,"errorMessage":"remove expired l0 files: %w","messagePattern":"remove expired l0 files: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compactor.go","lineNumber":409,"sourceCode":"\t\t}\n\n\t\tif info.MaxTXID <= maxL1TXID {\n\t\t\tdeleted = append(deleted, info)\n\t\t}\n\t}\n\n\tif processedAll && len(deleted) > 0 && lastInfo != nil && deleted[len(deleted)-1] == lastInfo {\n\t\tdeleted = deleted[:len(deleted)-1]\n\t}\n\n\tif len(deleted) == 0 {\n\t\treturn nil\n\t}\n\n\tif !c.RetentionEnabled {\n\t\tc.logger.Debug(\"skipping remote deletion (retention disabled)\", \"level\", 0, \"count\", len(deleted))\n\t} else if err := c.client.DeleteLTXFiles(ctx, deleted); err != nil {\n\t\treturn fmt.Errorf(\"remove expired l0 files: %w\", err)\n\t}\n\n\tif c.LocalFileDeleter != nil {\n\t\tfor _, info := range deleted {\n\t\t\tc.logger.Debug(\"deleting expired local l0 file\",\n\t\t\t\t\"minTXID\", info.MinTXID,\n\t\t\t\t\"maxTXID\", info.MaxTXID)\n\t\t\tif err := c.LocalFileDeleter(0, info.MinTXID, info.MaxTXID); err != nil {\n\t\t\t\tc.logger.Error(\"failed to remove local l0 file\", \"error\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tc.logger.Info(\"l0 retention enforced\", \"deleted_count\", len(deleted), \"max_l1_txid\", maxL1TXID)\n\n\treturn nil\n}\n","sourceCodeStart":391,"sourceCodeEnd":427,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/compactor.go#L391-L427","documentation":"After selecting expired L0 files, EnforceL0Retention deletes them via client.DeleteLTXFiles; failure is wrapped as 'remove expired l0 files: %w'. This blocks L0 cleanup so transactional LTX files will accumulate in remote storage until deletion succeeds.","triggerScenarios":"RetentionEnabled is true and the batch delete of expired L0 files errors — provider throttling on large batches, missing delete permission, network failure, or objects already deleted by a competing process.","commonSituations":"Thousands of accumulated L0 files making batch deletes hit provider limits; lifecycle rules already deleting files (conflict); IAM policy lacking multi-object delete; sustained network outage.","solutions":["Check the wrapped error — for throttling, ensure fewer files accumulate (fix L1 compaction) or use a provider supporting large batch deletes","Remove competing lifecycle rules or concurrent retention enforcers","Grant delete permissions on the replica prefix","Retry; the next monitorL0Retention cycle recomputes the expired set","If deletion must be handled externally, set RetentionEnabled=false and manage cleanup via cloud lifecycle policy"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Bound the batch size / ensure retention cadence so deletes stay small\nif len(expired) > maxBatch { expired = expired[:maxBatch] }","typeGuard":null,"tryCatchPattern":"if err := c.EnforceL0Retention(ctx, retention); err != nil {\n    var apiErr *provider.BatchDeleteError\n    if errors.As(err, &apiErr) && apiErr.Throttled { /* retry later */ }\n}","preventionTips":["Fix L1 compaction cadence so L0 files don't pile up","Remove competing lifecycle/expiry rules","Either Litestream or cloud policy owns cleanup — never both","Set RetentionEnabled=false only if external cleanup is guaranteed"],"tags":["storage","deletion","l0-retention"],"backgroundTag":"file-delete-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}