{"record":{"id":"18d5b34bcddbb0dc","repo":"benbjohnson/litestream","slug":"non-contiguous-ltx-file-level-d-current-s-nex","errorCode":null,"errorMessage":"non-contiguous ltx file: level=%d, current=%s, next=%s-%s","messagePattern":"non-contiguous ltx file: level=(.+?), current=(.+?), next=(.+?)-(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"vfs.go","lineNumber":2743,"sourceCode":"\n\tindex := make(map[uint32]ltx.PageIndexElem)\n\tmaxTXID := prevMaxTXID\n\tlastCommit := baseCommit\n\tnewCommit := baseCommit\n\treplaceIndex := false\n\n\tfor itr.Next() {\n\t\tinfo := itr.Item()\n\n\t\tf.mu.Lock()\n\t\tisNextTXID := info.MinTXID == maxTXID+1\n\t\tf.mu.Unlock()\n\t\tif !isNextTXID {\n\t\t\tif level == 0 && info.MinTXID > maxTXID+1 {\n\t\t\t\tf.logger.Warn(\"ltx gap detected at L0, deferring to higher levels\", \"expected\", maxTXID+1, \"next\", info.MinTXID)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn maxTXID, nil, newCommit, replaceIndex, fmt.Errorf(\"non-contiguous ltx file: level=%d, current=%s, next=%s-%s\", level, maxTXID, info.MinTXID, info.MaxTXID)\n\t\t}\n\n\t\tf.logger.Debug(\"new ltx file\", \"level\", info.Level, \"min\", info.MinTXID, \"max\", info.MaxTXID)\n\n\t\tidx, err := FetchPageIndex(ctx, f.client, info)\n\t\tif err != nil {\n\t\t\treturn maxTXID, nil, newCommit, replaceIndex, fmt.Errorf(\"fetch page index: %w\", err)\n\t\t}\n\t\thdr, err := FetchLTXHeader(ctx, f.client, info)\n\t\tif err != nil {\n\t\t\treturn maxTXID, nil, newCommit, replaceIndex, fmt.Errorf(\"fetch header: %w\", err)\n\t\t}\n\n\t\tif hdr.Commit < lastCommit {\n\t\t\treplaceIndex = true\n\t\t\tindex = make(map[uint32]ltx.PageIndexElem)\n\t\t}\n\t\tlastCommit = hdr.Commit","sourceCodeStart":2725,"sourceCodeEnd":2761,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2725-L2761","documentation":"While polling a level, an LTX file whose MinTXID does not continue the sequence (info.MinTXID != maxTXID+1) was found. The index would become inconsistent if applied, so the poll aborts with this error. L0 gaps are tolerated (deferred), other levels are hard failures.","triggerScenarios":"Replica storage at level > 0 has missing LTX files: files deleted out-of-band, partial snapshot restore, concurrent compaction race, or a storage lifecycle rule removing files.","commonSituations":"Manually pruning object storage; restoring a subset of LTX files; two litestream processes replicating to the same path; failed compaction leaving holes.","solutions":["Compare listed LTX files in storage against expected TXID sequence to find the gap","Run `litestream reset` (or the replica auto-recover option) to rebuild local state from the replica","Ensure only one litestream process replicates each database path","Disable object-store lifecycle policies that delete LTX files under retention"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// verify contiguity before relying on replica reads\ninfos := listLTXInfos(level)\nfor i := 1; i < len(infos); i++ {\n    if infos[i].MinTXID != infos[i-1].MaxTXID+1 {\n        return fmt.Errorf(\"gap at %s\", infos[i].MinTXID)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := pollOnce(ctx); err != nil {\n    var gapErr *ContiguityError // or strings.Contains \"non-contiguous ltx file\"\n    if strings.Contains(err.Error(), \"non-contiguous ltx file\") {\n        return resetAndRehydrate(dbPath) // litestream reset\n    }\n    return err\n}","preventionTips":["Never manually delete LTX files from replica storage","Ensure a single writer per database (use leasing)","Review retention/compaction settings before enabling lifecycle policies"],"tags":["replication","txid-gap","consistency"],"backgroundTag":"schema-validation-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"}