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

  1. Treat the remote LTX file as corrupt: re-replicate from the primary database or restore from an earlier TXID.
  2. Run `litestream ltx` to inspect available LTX files and pick an intact generation.
  3. If local state is corrupt relative to the replica, run `litestream reset` (or enable replica `auto-recover`).
  4. 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

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.