{"record":{"id":"f80b18a889beaa75","repo":"benbjohnson/litestream","slug":"detect-full-checkpoint-w","errorCode":null,"errorMessage":"detect full checkpoint: %w","messagePattern":"detect full checkpoint: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1809,"sourceCode":"\t} else if !lastPageMatch {\n\t\tinfo.reason = \"last page does not exist in last ltx file, wal overwritten by another process\"\n\t\treturn info, nil\n\t}\n\n\tdb.Logger.Debug(\"verify.2\", \"lastPageMatch\", lastPageMatch)\n\n\t// Salt has changed which could indicate a FULL checkpoint.\n\t// If we have a last page match, then we can assume that the WAL has not been overwritten.\n\tif !saltMatch {\n\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"wal restarted\",\n\t\t\t\"salt1\", salt1,\n\t\t\t\"salt2\", salt2)\n\n\t\tinfo.offset = WALHeaderSize\n\t\tinfo.salt1, info.salt2 = salt1, salt2\n\n\t\tif detected, err := db.detectFullCheckpoint(ctx, [][2]uint32{{salt1, salt2}, {dec.Header().WALSalt1, dec.Header().WALSalt2}}); err != nil {\n\t\t\treturn info, fmt.Errorf(\"detect full checkpoint: %w\", err)\n\t\t} else if detected {\n\t\t\tinfo.reason = \"full or restart checkpoint detected, snapshotting\"\n\t\t} else {\n\t\t\tinfo.snapshotting = false\n\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","sourceCodeStart":1791,"sourceCodeEnd":1827,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1791-L1827","documentation":"When WAL header salts differ from the LTX-recorded salts, verify calls detectFullCheckpoint() to decide whether a full/restart checkpoint rewrote the WAL or whether syncing can continue incrementally. Any error from that detection is wrapped here and aborts the sync cycle.","triggerScenarios":"Salt mismatch path taken in verifyWithExecutor and detectFullCheckpoint fails — it reads WAL/LTX data to compare page content, so any WAL read or LTX decode error propagates here.","commonSituations":"Aggressive checkpointing by the application combined with IO errors; corrupted LTX files preventing content comparison; concurrent access to the database by another process.","solutions":["Look at the wrapped inner error to distinguish WAL read vs LTX decode problems","If LTX files are corrupt, `litestream reset` and resnapshot","Reduce conflicting writers; ensure single-process ownership of the DB","Retry after fixing transient IO conditions"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    if strings.Contains(err.Error(), \"detect full checkpoint\") {\n        // transient IO: backoff and retry; corruption: reset\n    }\n}","preventionTips":["Tune checkpointing so full checkpoints don't storm the WAL","Maintain single-writer ownership of the database","Alert on repeated salt-mismatch snapshotting in litestream logs"],"tags":["wal","checkpoint","salt","detection"],"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-14T00:17:10.932Z"}