benbjohnson/litestream · error
ltx file too short to contain a page index: %d bytes
Error message
ltx file too short to contain a page index: %d bytes
What it means
Init validates that the bytes read from the replica are at least ltx.TrailerSize+8 long before slicing the trailer/page-index size off the end. If the replica returned a truncated or corrupt LTX file, this error is returned instead of panicking on an out-of-range slice. It means the remote LTX object does not contain a complete page index.
Source
Thrown at replica_client.go:138
offset = 0
}
f, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, offset, 0)
if err != nil {
return nil, fmt.Errorf("open ltx file: %w", err)
}
defer f.Close()
// If we have read the full size of the page index, return the page index block as a reader.
b, err := io.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("read ltx page index: %w", err)
}
// The replica may return fewer bytes than a page index footer occupies, for
// example if the file is truncated or otherwise corrupt. Slicing the footer
// off the end unchecked would index past the start of the buffer and panic.
if len(b) < ltx.TrailerSize+8 {
return nil, fmt.Errorf("ltx file too short to contain a page index: %d bytes", len(b))
}
size := binary.BigEndian.Uint64(b[len(b)-ltx.TrailerSize-8:])
if off := len(b) - int(size) - ltx.TrailerSize - 8; off > 0 {
return io.NopCloser(bytes.NewReader(b[off:])), nil
}
// Otherwise read the file from the start of the page index.
f, err = client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, info.Size-ltx.TrailerSize-8-int64(size), 0)
if err != nil {
return nil, fmt.Errorf("open ltx file: %w", err)
}
return f, nil
}
// FetchPage fetches and decodes a single page frame from an LTX file.
func FetchPage(ctx context.Context, client ReplicaClient, level int, minTXID, maxTXID ltx.TXID, offset, size int64) (ltx.PageHeader, []byte, error) {
f, err := client.OpenLTXFile(ctx, level, minTXID, maxTXID, offset, size)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Treat the remote LTX file as corrupt: re-replicate from the primary database or restore from an earlier TXID.
- Run `litestream ltx` to inspect available LTX files and pick an intact generation.
- If local state is corrupt relative to the replica, run `litestream reset` (or enable replica `auto-recover`).
- Check the upload path that produced the object for interruption (partial multipart uploads, disk-full, crashes).
Example fix
// before: blindly restoring latest
err := db.Restore(ctx, litestream.RestoreConfig{})
// after: detect corrupt LTX and fall back to an earlier backup
if strings.Contains(err.Error(), "too short to contain a page index") {
log.Printf("corrupt LTX at txid %d; attempting earlier backup", txid)
err = restoreAt(ctx, db, priorTxid)
} Defensive patterns
Strategy: fallback
Validate before calling
if info, err := statLTX(ctx, client, level, minTXID, maxTXID); err != nil || info.Size < ltx.TrailerSize+8 { return fmt.Errorf("ltx object too small (%d bytes); likely truncated", info.Size) } Try / catch
r, err := litestream.Init(ctx, client, level, minTXID, maxTXID)
if err != nil && strings.Contains(err.Error(), "too short to contain a page index") {
log.Printf("corrupt/truncated LTX %d/%d; falling back to earlier backup", level, minTXID)
return restoreFromEarlierBackup(ctx)
} Prevention
- Ensure uploads are atomic (complete multipart uploads; temp-file + rename for file:// replicas).
- Verify object size/ETag after upload matches the local LTX file.
- Avoid manual copying/tampering with replica objects.
- Alert on disk-full conditions for file-backed replicas.
When it happens
Trigger: Calling Init against an LTX file whose stored body is shorter than the trailer plus the 8-byte size field — e.g. the object was truncated by a failed upload or corrupt lifecycle/replication copy.
Common situations: Interrupted uploads leaving partial objects in S3; manual file tampering or bad sync tools copying LTX files; disk-full conditions on file:// replicas; provider bugs or mismatched multi-part upload aborts.
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
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/f162323aec5f1180.
Report an issue: GitHub.