{"record":{"id":"8073e2bdf46de522","repo":"benbjohnson/litestream","slug":"decode-ltx-page-w","errorCode":null,"errorMessage":"decode ltx page: %w","messagePattern":"decode ltx page: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1850,"sourceCode":"\t\treturn false, fmt.Errorf(\"cannot read last synced wal page: %w\", err)\n\t}\n\tpgno := binary.BigEndian.Uint32(frame[0:])\n\tfsalt1 := binary.BigEndian.Uint32(frame[8:])\n\tfsalt2 := binary.BigEndian.Uint32(frame[12:])\n\tdata := frame[WALFrameHeaderSize:]\n\n\tif fsalt1 != dec.Header().WALSalt1 || fsalt2 != dec.Header().WALSalt2 {\n\t\treturn false, nil\n\t}\n\n\t// Verify that the last page in the WAL exists in the last LTX file.\n\tbuf := make([]byte, dec.Header().PageSize)\n\tfor {\n\t\tvar hdr ltx.PageHeader\n\t\tif err := dec.DecodePage(&hdr, buf); errors.Is(err, io.EOF) {\n\t\t\treturn false, nil // page not found in LTX file\n\t\t} else if err != nil {\n\t\t\treturn false, fmt.Errorf(\"decode ltx page: %w\", err)\n\t\t}\n\n\t\tif pgno != hdr.Pgno {\n\t\t\tcontinue // page number doesn't match\n\t\t}\n\t\tif !bytes.Equal(data, buf) {\n\t\t\tcontinue // page data doesn't match\n\t\t}\n\t\treturn true, nil // Page matches\n\t}\n}\n\n// detectFullCheckpoint attempts to detect checks if a FULL or RESTART checkpoint\n// has occurred and we may have missed some frames.\nfunc (db *DB) detectFullCheckpoint(ctx context.Context, knownSalts [][2]uint32) (bool, error) {\n\twalFile, err := os.Open(db.WALPath())\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"open wal file: %w\", err)","sourceCodeStart":1832,"sourceCodeEnd":1868,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1832-L1868","documentation":"While scanning the last LTX file page-by-page during lastPageMatch, dec.DecodePage failed with a non-EOF error, meaning the LTX payload is malformed or unreadable. EOF is normal (page not found); any other error indicates corruption of the LTX file.","triggerScenarios":"DecodePage returns an error other than io.EOF while searching for the page number at prevWALOffset — compressed or encrypted payload corruption, truncated LTX body after a valid header.","commonSituations":"Partial LTX file from a crash or ENOSPC during write; storage-level bit rot; version mismatch between the LTX writer and reader.","solutions":["`litestream reset <db>` to discard the corrupted local LTX and resnapshot","Verify the replica copy is intact and restore from it if needed (or enable auto-recover: true)","Check disk space and storage health at the time of the error","Confirm litestream versions match across any manual LTX handling"],"exampleFix":"// config before\nreplicas:\n  - url: s3://bucket/db\n// config after\nreplicas:\n  - url: s3://bucket/db\n    auto-recover: true","handlingStrategy":"fallback","validationCode":"// scan-verify the LTX body after any crash/ENOSPC\nfor {\n    var hdr ltx.PageHeader\n    if err := dec.DecodePage(&hdr, buf); err != nil {\n        if errors.Is(err, io.EOF) { break }\n        // corrupt: trigger reset/restore\n    }\n}","typeGuard":"func ltxDecodable(path string) bool {\n    f, err := os.Open(path); if err != nil { return false }\n    defer f.Close()\n    dec := ltx.NewDecoder(f)\n    return dec.DecodeHeader() == nil\n}","tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    if strings.Contains(err.Error(), \"decode ltx page\") {\n        // corrupted LTX: reset local state, resnapshot from replica\n    }\n}","preventionTips":["Enable auto-recover: true so corruption triggers automatic reset","Keep adequate free disk space to avoid truncated writes","Validate LTX files with `litestream ltx` after incidents"],"tags":["ltx","decode","page","corruption"],"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"}