{"record":{"id":"c52c0a34f3abc9c5","repo":"benbjohnson/litestream","slug":"non-contiguous-ltx-files-have-up-to-s-but-next-f","errorCode":null,"errorMessage":"non-contiguous ltx files: have up to %s but next file starts at %s","messagePattern":"non-contiguous ltx files: have up to (.+?) but next file starts at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"replica.go","lineNumber":1617,"sourceCode":"\t\tlogger.Debug(\"matching LTX file for restore\",\n\t\t\t\"filename\", ltx.FormatFilename(next.candidate.MinTXID, next.candidate.MaxTXID),\n\t\t\t\"level\", next.candidate.Level)\n\t\tinfos = append(infos, next.candidate)\n\t\tcurrentMax = next.candidate.MaxTXID\n\t\tnext.candidate = nil\n\n\t\tif txID != 0 && currentMax >= txID {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif len(infos) > 0 && txID == 0 && timestamp.IsZero() {\n\t\tfor _, cursor := range cursors {\n\t\t\tif err := cursor.ensureCurrent(); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif cursor.current != nil && cursor.current.MinTXID > currentMax+1 {\n\t\t\t\treturn nil, fmt.Errorf(\"non-contiguous ltx files: have up to %s but next file starts at %s\", currentMax, cursor.current.MinTXID)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(infos) == 0 {\n\t\treturn nil, ErrTxNotAvailable\n\t}\n\tif txID != 0 && infos.MaxTXID() < txID {\n\t\treturn nil, ErrTxNotAvailable\n\t}\n\n\treturn infos, nil\n}\n\ntype restoreLevelCursor struct {\n\t// itr streams LTX file infos for a single level in filename order.\n\titr ltx.FileIterator\n\t// current holds the last item read from itr but not yet evaluated.","sourceCodeStart":1599,"sourceCodeEnd":1635,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica.go#L1599-L1635","documentation":"During a latest-restore plan (no explicit TXID/timestamp), CalcRestorePlan validates that the LTX file sequence is contiguous. If the next available file's MinTXID is greater than currentMax+1, there is a gap in the replicated LTX chain and restoring the full sequence is impossible without data loss, so the plan is rejected.","triggerScenarios":"Restoring 'latest' when generation files were deleted out of order (lifecycle policy, manual rm, partial upload) or when files from a rolled/compacted level are missing, leaving TXID gaps.","commonSituations":"Cloud storage retention rules deleting older LTX files while newer ones remain; interrupted compaction; copying replica data manually and skipping files; restoring across generations with a missing boundary file.","solutions":["Run `litestream ltx -level all` on the replica to find the missing TXID range, then re-replicate or restore the missing files.","Use `litestream reset` if local LTX state is corrupted, then let replication rebuild.","Restore to an older timestamp/TXID before the gap instead of latest.","Fix storage lifecycle/retention settings so mid-chain files are not deleted; disable retention only when a cloud lifecycle policy owns cleanup."],"exampleFix":"// before\n$ litestream restore -replica s3 db.sqlite\n// error: non-contiguous ltx files: have up to 0000000000000123 but next file starts at 0000000000000200\n// after\n// restore to a point before the gap:\n$ litestream restore -timestamp 2024-06-01T00:00:00Z db.sqlite","handlingStrategy":"validation","validationCode":"// Go: pre-validate TXID contiguity before a latest restore\ninfos, err := FindLTXFiles(ctx, client, 0, true, nil)\nif err != nil { return err }\nfor i := 1; i < len(infos); i++ {\n    if infos[i].MinTXID > infos[i-1].MaxTXID+1 {\n        return fmt.Errorf(\"gap between %d and %d\", infos[i-1].MaxTXID, infos[i].MinTXID)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := restoreLatest(); err != nil {\n    if strings.HasPrefix(err.Error(), \"non-contiguous ltx files\") {\n        // restore to a timestamp/TXID before the gap instead\n        return restoreWithTimestamp(lastKnownGoodTime)\n    }\n    return err\n}","preventionTips":["Never manually delete or copy subsets of LTX files in the replica.","Ensure lifecycle/retention rules delete whole chains atomically, not middle files.","Run `litestream ltx -level all` as a periodic integrity check.","Use `litestream reset` only when local LTX state is actually corrupted."],"tags":["restore","ltx","data-integrity","gap"],"backgroundTag":"checksum-mismatch","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"}