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
- Identify and delete the corrupt LTX object, then re-restore from an intact generation, or run 'litestream reset' to clear corrupted local LTX state
- Verify the replica storage contents — the object should start with the LTX magic bytes; an empty or foreign file must be removed
- If the local WAL/replica state is suspect, use 'litestream restore' from a known-good replica copy
- 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
- Skip zero-length or non-LTX objects when choosing restore candidates
- Never write non-LTX files into the replica storage prefix
- Ensure uploads complete before publishing (atomic object creation where supported)
- Use 'litestream reset' to clear corrupted local LTX state instead of retrying blindly
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
- verify
- extract timestamp from LTX header: %w
- decode page: %w
- extract timestamp from LTX header: %w
- decode ltx header: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/f7389ab65c443435.
Report an issue: GitHub.