benbjohnson/litestream · error
decode page: %w
Error message
decode page: %w
What it means
ApplyLTX reads each page of the LTX file with dec.DecodePage into a pageSize buffer; io.EOF ends the loop, any other error becomes this wrapper. It means a page record inside the LTX stream is malformed, the stream is truncated mid-page, or the checksum failed — the file cannot be applied incrementally.
Source
Thrown at vfs.go:865
}
defer rc.Close()
dec := ltx.NewDecoder(rc)
if err := dec.DecodeHeader(); err != nil {
return fmt.Errorf("decode header: %w", err)
}
h.mu.Lock()
defer h.mu.Unlock()
// Apply each page to the hydration file
for {
var phdr ltx.PageHeader
data := make([]byte, h.pageSize)
if err := dec.DecodePage(&phdr, data); err == io.EOF {
break
} else if err != nil {
return fmt.Errorf("decode page: %w", err)
}
off := int64(phdr.Pgno-1) * int64(h.pageSize)
if _, err := h.file.WriteAt(data, off); err != nil {
return fmt.Errorf("write page %d: %w", phdr.Pgno, err)
}
}
return nil
}
// ReadAt reads data from the hydrated local file.
func (h *Hydrator) ReadAt(p []byte, off int64) (int, error) {
h.mu.Lock()
n, err := h.file.ReadAt(p, off)
h.mu.Unlock()
if err != nil && err != io.EOF {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify page size consistency: the LTX header's page size must equal h.pageSize; re-create the hydration file with the matching page size.
- Re-download and checksum the LTX file; replace corrupt replica objects by forcing a new snapshot.
- Fall back to a full Restore (Hydrator.Restore) instead of incremental ApplyLTX when any file fails to decode.
- Check the storage layer for truncated objects (Content-Length vs actual bytes) and fix the upload path.
Example fix
// before
data := make([]byte, h.pageSize)
if err := dec.DecodePage(&phdr, data); err == io.EOF {
break
} else if err != nil { return fmt.Errorf("decode page: %w", err) }
// after
data := make([]byte, h.pageSize)
if err := dec.DecodePage(&phdr, data); err == io.EOF {
break
} else if err != nil {
return fmt.Errorf("decode page: %w (pageSize=%d, header pageSize=%d)", err, h.pageSize, dec.Header().PageSize)
} Defensive patterns
Strategy: validation
Validate before calling
if dec.Header().PageSize != h.pageSize {
return fmt.Errorf("page size mismatch: ltx=%d hydrator=%d", dec.Header().PageSize, h.pageSize)
} Try / catch
if err := dec.DecodePage(&phdr, data); err != nil && err != io.EOF {
return fmt.Errorf("corrupt ltx page %d: %w", phdr.Pgno, err)
} Prevention
- Keep -page-size consistent across backup, restore, and hydration configs
- Validate replica object checksums after download
- Fall back to full restore when any incremental page fails to decode
When it happens
Trigger: Calling ApplyLTX on a corrupt or truncated LTX file, a page whose size doesn't match h.pageSize (page-size mismatch between writer and hydration file), or stream corruption surfaced mid-decode.
Common situations: Interrupted uploads producing truncated LTX objects; databases backed up with a different -page-size than the hydration file was created with; bit-rot or CDN-transferred corruption.
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
- decode
- decode ltx page: %w
- decode header: %w
- page size mismatch: got %d, expected %d
- unsupported page size: %d
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/5d96eb825a80900c.
Report an issue: GitHub.