{"record":{"id":"9a400d4e45ff2fe7","repo":"benbjohnson/litestream","slug":"close-iterator-w","errorCode":null,"errorMessage":"close iterator: %w","messagePattern":"close iterator: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compactor.go","lineNumber":229,"sourceCode":"\t\t\tcontinue\n\t\t}\n\n\t\t// Check for TXID contiguity: prev.MaxTXID + 1 should equal curr.MinTXID\n\t\texpectedMinTXID := prevInfo.MaxTXID + 1\n\t\tif info.MinTXID != expectedMinTXID {\n\t\t\tif info.MinTXID > expectedMinTXID {\n\t\t\t\treturn fmt.Errorf(\"TXID gap detected: prev.MaxTXID=%s, next.MinTXID=%s (expected %s)\",\n\t\t\t\t\tprevInfo.MaxTXID, info.MinTXID, expectedMinTXID)\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"TXID overlap detected: prev.MaxTXID=%s, next.MinTXID=%s\",\n\t\t\t\tprevInfo.MaxTXID, info.MinTXID)\n\t\t}\n\n\t\tprevInfo = info\n\t}\n\n\tif err := itr.Close(); err != nil {\n\t\treturn fmt.Errorf(\"close iterator: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// EnforceSnapshotRetention enforces retention of snapshot level files by timestamp.\n// Files older than the retention duration are deleted (except the newest is always kept).\n// Returns the minimum snapshot TXID still retained (useful for cascading retention to lower levels).\nfunc (c *Compactor) EnforceSnapshotRetention(ctx context.Context, retention time.Duration) (ltx.TXID, error) {\n\ttimestamp := time.Now().Add(-retention)\n\tc.logger.Debug(\"enforcing snapshot retention\", \"timestamp\", timestamp)\n\n\titr, err := c.client.LTXFiles(ctx, SnapshotLevel, 0, false)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"fetch ltx files: %w\", err)\n\t}\n\tdefer itr.Close()\n","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/compactor.go#L211-L247","documentation":"VerifyLevelConsistency finished walking a compaction level's LTX files and closing the iterator returned a non-nil error; the check fails with 'close iterator: <err>'. This guards the invariant that a level's files chain correctly (prev TXID linkage), and a close error means the underlying storage client couldn't cleanly finish the listing, so verification results cannot be trusted.","triggerScenarios":"Called from Compact after iterating level files via c.client.LTXFiles; the storage backend's iterator Close fails — e.g. S3 listing session teardown error, network reset during final pagination flush, or context canceled while closing.","commonSituations":"Flaky network to the replica storage during compaction; context deadline/cancelation from the caller of Compact; misbehaving custom ReplicaClient whose Close implementation errors.","solutions":["Inspect the wrapped inner error — network issues usually resolve on retry of the compaction","Verify storage backend connectivity/credentials (S3 etc.)","Check that a custom ReplicaClient's LTXFiles iterator Close is implemented correctly (must release resources, return nil on clean close)","If caused by context cancelation, ensure the parent context has enough time/budget for compaction"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// before compaction, sanity-check storage reachability:\nitr, err := client.LTXFiles(ctx, level, 0, false)\nif err != nil { return err }\nitr.Close()","typeGuard":null,"tryCatchPattern":"if err := compactor.Compact(ctx); err != nil {\n    var retryable bool\n    if strings.Contains(err.Error(), \"close iterator\") && errors.Is(ctx.Err(), nil) {\n        retryable = true // transient storage error — backoff and retry\n    }\n}","preventionTips":["Give compaction contexts enough budget to avoid cancelation during close","Test custom ReplicaClient iterators: Close must be idempotent and error only on real failures","Monitor storage backend health before compaction windows","Retry compaction with backoff on transient network errors"],"tags":["storage","ltx","iteration","network"],"backgroundTag":"network-request-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}