{"record":{"id":"50f8fc43e8674e6a","repo":"benbjohnson/litestream","slug":"cannot-read-last-synced-wal-page-w","errorCode":null,"errorMessage":"cannot read last synced wal page: %w","messagePattern":"cannot read last synced wal page: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1832,"sourceCode":"\t\t}\n\n\t\treturn info, nil\n\t}\n\n\tinfo.snapshotting = false\n\n\treturn info, nil\n}\n\n// lastPageMatch checks if the last page read in the WAL exists in the last LTX file.\nfunc (db *DB) lastPageMatch(ctx context.Context, dec *ltx.Decoder, prevWALOffset, frameSize int64) (bool, error) {\n\tif prevWALOffset <= WALHeaderSize {\n\t\treturn false, nil\n\t}\n\n\tframe, err := readWALFileAt(db.WALPath(), prevWALOffset, frameSize)\n\tif err != nil {\n\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)","sourceCodeStart":1814,"sourceCodeEnd":1850,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1814-L1850","documentation":"lastPageMatch reads the WAL frame at the previously synced offset via readWALFileAt to compare it against LTX content. A short read, missing WAL, or IO error is wrapped as this error and causes verify to abort the incremental path.","triggerScenarios":"readWALFileAt(db.WALPath(), prevWALOffset, frameSize) fails — WAL truncated below prevWALOffset, WAL deleted, or read/IO error at that offset.","commonSituations":"Checkpoint truncated the WAL between syncs while state said syncedToWALEnd was false; external process manipulating the WAL; disk errors.","solutions":["Check WAL file size vs prevWALOffset (`ls -l db-wal`); a truncated WAL indicates a checkpoint raced the sync","Verify no external processes touch the WAL file","Fix filesystem/IO issues surfaced in the wrapped error","`litestream reset` if local state is persistently inconsistent with the WAL"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"fi, err := os.Stat(dbPath + \"-wal\")\nif err == nil && fi.Size() < prevWALOffset { /* WAL truncated under expected offset */ }","typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    if strings.Contains(err.Error(), \"cannot read last synced wal page\") {\n        // checkpoint raced sync: backoff-retry; persistent: reset state\n    }\n}","preventionTips":["Accept that checkpoints truncate the WAL; let litestream resnapshot when detected","Monitor WAL size vs sync lag","Avoid external WAL truncation tools"],"tags":["wal","frame","read","offset"],"backgroundTag":"file-read-failed","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"}