benbjohnson/litestream · critical
non-contiguous ltx files: have up to %s but next file starts
Error message
non-contiguous ltx files: have up to %s but next file starts at %s
What it means
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.
Source
Thrown at replica.go:1617
logger.Debug("matching LTX file for restore",
"filename", ltx.FormatFilename(next.candidate.MinTXID, next.candidate.MaxTXID),
"level", next.candidate.Level)
infos = append(infos, next.candidate)
currentMax = next.candidate.MaxTXID
next.candidate = nil
if txID != 0 && currentMax >= txID {
break
}
}
if len(infos) > 0 && txID == 0 && timestamp.IsZero() {
for _, cursor := range cursors {
if err := cursor.ensureCurrent(); err != nil {
return nil, err
}
if cursor.current != nil && cursor.current.MinTXID > currentMax+1 {
return nil, fmt.Errorf("non-contiguous ltx files: have up to %s but next file starts at %s", currentMax, cursor.current.MinTXID)
}
}
}
if len(infos) == 0 {
return nil, ErrTxNotAvailable
}
if txID != 0 && infos.MaxTXID() < txID {
return nil, ErrTxNotAvailable
}
return infos, nil
}
type restoreLevelCursor struct {
// itr streams LTX file infos for a single level in filename order.
itr ltx.FileIterator
// current holds the last item read from itr but not yet evaluated.View on GitHub (pinned to 4ed7a308f6)
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.
Example fix
// before $ litestream restore -replica s3 db.sqlite // error: non-contiguous ltx files: have up to 0000000000000123 but next file starts at 0000000000000200 // after // restore to a point before the gap: $ litestream restore -timestamp 2024-06-01T00:00:00Z db.sqlite
Defensive patterns
Strategy: validation
Validate before calling
// Go: pre-validate TXID contiguity before a latest restore
infos, err := FindLTXFiles(ctx, client, 0, true, nil)
if err != nil { return err }
for i := 1; i < len(infos); i++ {
if infos[i].MinTXID > infos[i-1].MaxTXID+1 {
return fmt.Errorf("gap between %d and %d", infos[i-1].MaxTXID, infos[i].MinTXID)
}
} Try / catch
if err := restoreLatest(); err != nil {
if strings.HasPrefix(err.Error(), "non-contiguous ltx files") {
// restore to a timestamp/TXID before the gap instead
return restoreWithTimestamp(lastKnownGoodTime)
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- verify
- created at: %w
- decode page: %w
- missing WAL segment: expected %d/%d, got %d/%d
- get LTX time bounds: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/c52c0a34f3abc9c5.
Report an issue: GitHub.