{"record":{"id":"5d96eb825a80900c","repo":"benbjohnson/litestream","slug":"decode-page-w-5d96eb","errorCode":null,"errorMessage":"decode page: %w","messagePattern":"decode page: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":865,"sourceCode":"\t}\n\tdefer rc.Close()\n\n\tdec := ltx.NewDecoder(rc)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\treturn fmt.Errorf(\"decode header: %w\", err)\n\t}\n\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\n\t// Apply each page to the hydration file\n\tfor {\n\t\tvar phdr ltx.PageHeader\n\t\tdata := make([]byte, h.pageSize)\n\t\tif err := dec.DecodePage(&phdr, data); err == io.EOF {\n\t\t\tbreak\n\t\t} else if err != nil {\n\t\t\treturn fmt.Errorf(\"decode page: %w\", err)\n\t\t}\n\n\t\toff := int64(phdr.Pgno-1) * int64(h.pageSize)\n\t\tif _, err := h.file.WriteAt(data, off); err != nil {\n\t\t\treturn fmt.Errorf(\"write page %d: %w\", phdr.Pgno, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// ReadAt reads data from the hydrated local file.\nfunc (h *Hydrator) ReadAt(p []byte, off int64) (int, error) {\n\th.mu.Lock()\n\tn, err := h.file.ReadAt(p, off)\n\th.mu.Unlock()\n\n\tif err != nil && err != io.EOF {","sourceCodeStart":847,"sourceCodeEnd":883,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L847-L883","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ndata := make([]byte, h.pageSize)\nif err := dec.DecodePage(&phdr, data); err == io.EOF {\n    break\n} else if err != nil { return fmt.Errorf(\"decode page: %w\", err) }\n// after\ndata := make([]byte, h.pageSize)\nif err := dec.DecodePage(&phdr, data); err == io.EOF {\n    break\n} else if err != nil {\n    return fmt.Errorf(\"decode page: %w (pageSize=%d, header pageSize=%d)\", err, h.pageSize, dec.Header().PageSize)\n}","handlingStrategy":"validation","validationCode":"if dec.Header().PageSize != h.pageSize {\n    return fmt.Errorf(\"page size mismatch: ltx=%d hydrator=%d\", dec.Header().PageSize, h.pageSize)\n}","typeGuard":null,"tryCatchPattern":"if err := dec.DecodePage(&phdr, data); err != nil && err != io.EOF {\n    return fmt.Errorf(\"corrupt ltx page %d: %w\", phdr.Pgno, err)\n}","preventionTips":["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"],"tags":["ltx","decode","corruption","page-size"],"backgroundTag":"checksum-mismatch","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}