benbjohnson/litestream · error

peek header: %w

Error message

peek header: %w

What it means

After successfully opening the LTX file, FetchLTXHeader calls ltx.PeekHeader to read and decode the LTX header bytes. This error means the header could not be read or decoded: the file is empty, truncated, or its first bytes are not a valid LTX header — i.e. the stored object is corrupt or not an LTX file at all.

Source

Thrown at replica_client.go:109

	rc, err := fetchPageIndexData(ctx, client, info)
	if err != nil {
		return nil, err
	}
	defer rc.Close()

	return ltx.DecodePageIndex(bufio.NewReader(rc), info.Level, info.MinTXID, info.MaxTXID)
}

// FetchLTXHeader reads & returns the LTX header for the given file info.
func FetchLTXHeader(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (ltx.Header, error) {
	rc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)
	if err != nil {
		return ltx.Header{}, fmt.Errorf("open ltx file: %w", err)
	}
	defer rc.Close()
	hdr, _, err := ltx.PeekHeader(rc)
	if err != nil {
		return ltx.Header{}, fmt.Errorf("peek header: %w", err)
	}
	return hdr, nil
}

// fetchPageIndexData fetches a chunk of the end of the file to get the page index.
// If the fetch was smaller than the actual page index, another call is made to fetch the rest.
func fetchPageIndexData(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (io.ReadCloser, error) {
	// Fetch the end of the file to get the page index.
	offset := info.Size - DefaultEstimatedPageIndexSize
	if offset < 0 {
		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()

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Identify and delete the corrupt LTX object, then re-restore from an intact generation, or run 'litestream reset' to clear corrupted local LTX state
  2. Verify the replica storage contents — the object should start with the LTX magic bytes; an empty or foreign file must be removed
  3. If the local WAL/replica state is suspect, use 'litestream restore' from a known-good replica copy
  4. Check for processes writing non-LTX files into the replica path and stop them

Example fix

# before: repeatedly retrying restore against a corrupt object
litestream restore -o my.db mydb.db   # fails with peek header
# after: clear corrupted local state and re-restore
litestream reset mydb.db
litestream restore -o my.db mydb.db
Defensive patterns

Strategy: validation

Validate before calling

// Verify the stored object is a non-empty LTX file before decoding
rc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)
if err == nil {
    magic := make([]byte, 4)
    io.ReadFull(rc, magic)
    rc.Close()
    if !bytes.Equal(magic, []byte("LTX1")) {
        return fmt.Errorf("object %s is not a valid LTX file", info.MaxTXID)
    }
}

Type guard

func looksLikeLTX(ctx context.Context, c ReplicaClient, info *ltx.FileInfo) bool {
    rc, err := c.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)
    if err != nil { return false }
    defer rc.Close()
    hdr, _, err := ltx.PeekHeader(rc)
    return err == nil && hdr.IsValid()
}

Try / catch

hdr, err := FetchLTXHeader(ctx, client, info)
if err != nil {
    if strings.Contains(err.Error(), "peek header") {
        // corrupt/truncated object: skip it and choose another restore candidate
    }
    return err
}

Prevention

When it happens

Trigger: client.OpenLTXFile succeeded but PeekHeader fails: zero-byte or truncated object in storage (aborted upload), object overwritten with non-LTX data (wrong content written to the replica prefix), network read cut off mid-header for remote stores, or a corrupt local LTX file.

Common situations: Interrupted uploads leaving partial objects in the bucket; someone/something wrote other files into the replica path; disk corruption on the replica volume; restoring from a replica where a compaction crash left a truncated 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


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/f7389ab65c443435. Report an issue: GitHub.