benbjohnson/litestream · error
decode ltx header: %w
Error message
decode ltx header: %w
What it means
Litestream opened the LTX file but the ltx.Decoder failed while decoding its header. This means the bytes at the start of the object are not a valid LTX header — corruption, truncation, or the object is not actually an LTX file.
Source
Thrown at vfs.go:2812
return 0, fmt.Errorf("unsupported page size: %d", pageSize)
}
return pageSize, nil
}
if lastErr != nil {
return 0, fmt.Errorf("read ltx header: %w", lastErr)
}
return 0, fmt.Errorf("no ltx file available to determine page size")
}
func readPageSizeFromInfo(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (uint32, error) {
rc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)
if err != nil {
return 0, fmt.Errorf("open ltx file: %w", err)
}
defer rc.Close()
dec := ltx.NewDecoder(rc)
if err := dec.DecodeHeader(); err != nil {
return 0, fmt.Errorf("decode ltx header: %w", err)
}
return dec.Header().PageSize, nil
}
func isSupportedPageSize(pageSize uint32) bool {
switch pageSize {
case 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536:
return true
default:
return false
}
}
func (f *VFSFile) waitForRestorePlan() ([]*ltx.FileInfo, error) {
// If write mode is enabled, don't wait - return immediately so we can
// create a new database if no files exist.
if f.writeEnabled {
infos, err := CalcRestorePlan(f.ctx, f.client, 0, time.Time{}, f.logger)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Delete the corrupted LTX object and re-replicate from the source database
- Run litestream reset to clear local state and force a fresh snapshot
- Verify with litestream ltx that the object parses and check its header checksum
- Check LTX_FORMAT.md for format-version compatibility between writer and reader versions
Example fix
// before: truncated object fails DecodeHeader // after: validate remotely before restore $ litestream ltx -level all s3://bucket/path # list + verify each LTX file $ litestream restore -output db.sqlite db.yml
Defensive patterns
Strategy: try-catch
Try / catch
if err := openVFS(ctx); err != nil {
if strings.Contains(err.Error(), "decode ltx header") {
return quarantineAndReset(ctx) // litestream reset + re-replicate
}
return err
} Prevention
- Avoid interrupting replicate mid-upload (partial objects)
- Verify objects with litestream ltx before restore
- Never place non-LTX files under the replica prefix
- Keep writer/reader Litestream versions compatible
When it happens
Trigger: readPageSizeFromInfo calling dec.DecodeHeader() on a stream whose header is invalid: partial uploads, objects overwritten/corrupted by another writer, or a non-LTX object stored under the replica prefix.
Common situations: Interrupted uploads leaving truncated objects; manually copied/renamed files in the bucket; version-mismatch where an old LTX format is read by a newer reader (upgrade artifacts); proxy/storage layer returning an HTML error page instead of object bytes.
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
- extract timestamp from LTX header: %w
- peek header: %w
- extract timestamp from LTX header: %w
- invalid replica, checksum mismatch
- ltx file corrupted
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/d362639c4db34b28.
Report an issue: GitHub.