{"record":{"id":"4816c11b7753bbb4","repo":"benbjohnson/litestream","slug":"enforce-l-d-retention-w","errorCode":null,"errorMessage":"enforce L%d retention: %w","messagePattern":"enforce L(.+?) retention: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"store.go","lineNumber":850,"sourceCode":"\n// EnforceSnapshotRetention removes old snapshots by timestamp and then\n// cleans up all lower levels based on minimum snapshot TXID.\nfunc (s *Store) EnforceSnapshotRetention(ctx context.Context, db *DB) error {\n\t// Enforce retention for the snapshot level.\n\tminSnapshotTXID, err := db.EnforceSnapshotRetention(ctx, time.Now().Add(-s.SnapshotRetention))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"enforce snapshot retention: %w\", err)\n\t}\n\n\t// We should also enforce retention for L0 on the same schedule as L1.\n\tfor _, lvl := range s.levels {\n\t\t// Skip L0 since it is enforced on a more frequent basis.\n\t\tif lvl.Level == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tif err := db.EnforceRetentionByTXID(ctx, lvl.Level, minSnapshotTXID); err != nil {\n\t\t\treturn fmt.Errorf(\"enforce L%d retention: %w\", lvl.Level, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ValidationResult holds the result of validating a replica's LTX files.\ntype ValidationResult struct {\n\tValid  bool              // true if no errors found\n\tErrors []ValidationError // all errors found\n}\n\n// Validate checks LTX file consistency across all databases and levels.\n// SnapshotLevel (9) is excluded since snapshots are not contiguous.\nfunc (s *Store) Validate(ctx context.Context) (*ValidationResult, error) {\n\tresult := &ValidationResult{Valid: true}\n\n\ts.mu.Lock()","sourceCodeStart":832,"sourceCodeEnd":868,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/store.go#L832-L868","documentation":"Store.EnforceSnapshotRetention failed enforcing retention at a non-snapshot compaction level: db.EnforceRetentionByTXID(ctx, lvl.Level, minSnapshotTXID) errored while deleting LTX files below the minimum retained snapshot TXID. This cascades the snapshot retention decision down to levels L1+. The wrapped storage error is returned with %w including the level number.","triggerScenarios":"EnforceSnapshotRetention(ctx, db) where EnforceRetentionByTXID for any level in s.levels (L1, L2, ...) fails: LTXFiles listing error or delete failure on the replica backend for that level.","commonSituations":"IAM credentials lacking delete rights on level prefixes, provider immutability/object-lock settings, partial storage outage affecting specific prefixes, or network interruption during a multi-level prune.","solutions":["Check which level appears in the message (e.g. \"enforce L1 retention\") and test list/delete access on that storage prefix.","Grant delete permissions to the replica credentials; remove object-lock constraints that block cleanup.","Retry; retention enforcement runs on a schedule and is idempotent.","Verify replica contents are intact (no manually deleted level prefixes) — a missing level can break iteration."],"exampleFix":"// before\nif err := db.EnforceRetentionByTXID(ctx, lvl.Level, minSnapshotTXID); err != nil {\n    return fmt.Errorf(\"enforce L%d retention: %w\", lvl.Level, err)\n}\n// after\nif err := db.EnforceRetentionByTXID(ctx, lvl.Level, minSnapshotTXID); err != nil {\n    if ctx.Err() != nil {\n        return ctx.Err() // propagate shutdown/cancel instead of storage error\n    }\n    return fmt.Errorf(\"enforce L%d retention: %w\", lvl.Level, err)\n}","handlingStrategy":"retry","validationCode":"for _, lvl := range []int{1, 2} {\n    itr, err := client.LTXFiles(ctx, lvl, 0, false)\n    if err != nil {\n        return fmt.Errorf(\"level %d not listable: %w\", lvl, err)\n    }\n    itr.Close()\n}","typeGuard":null,"tryCatchPattern":"err := store.EnforceSnapshotRetention(ctx, db)\nif err != nil {\n    var lvl int\n    if _, scanErr := fmt.Sscanf(err.Error(), \"enforce L%d retention\", &lvl); scanErr == nil {\n        log.Printf(\"retention failed at level %d; check delete perms on that prefix\", lvl)\n    }\n    return err\n}","preventionTips":["Ensure delete permissions across all level prefixes (L1, L2, snapshot)","Avoid manual deletion of level prefixes on the replica","Retry retention after transient network failures; it is idempotent","Alert on repeated retention failures to avoid unbounded storage growth"],"tags":["go","litestream","retention","storage"],"backgroundTag":"permission-denied","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"}