{"record":{"id":"656b377f3e0bd80e","repo":"benbjohnson/litestream","slug":"read-page-d-d-w","errorCode":null,"errorMessage":"read page %d @ %d: %w","messagePattern":"read page (.+?) @ (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2309,"sourceCode":"\n\tfor pgno := uint32(1); pgno <= commit; pgno++ {\n\t\tif pgno == lockPgno {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Check if the caller has canceled during processing.\n\t\tselect {\n\t\tcase <-ctx.Done():\n\t\t\treturn context.Cause(ctx)\n\t\tdefault:\n\t\t}\n\n\t\t// If page exists in the WAL, read from there.\n\t\tif offset, ok := pageMap[pgno]; ok {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from wal\", \"txid\", enc.Header().MinTXID, \"offset\", offset, \"pgno\", pgno, \"type\", \"db+wal\")\n\n\t\t\tif n, err := walFile.ReadAt(data, offset+WALFrameHeaderSize); err != nil {\n\t\t\t\treturn fmt.Errorf(\"read page %d @ %d: %w\", pgno, offset, err)\n\t\t\t} else if n != len(data) {\n\t\t\t\treturn fmt.Errorf(\"short read page %d @ %d\", pgno, offset)\n\t\t\t}\n\n\t\t\tif err := enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {\n\t\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\toffset := int64(pgno-1) * int64(db.pageSize)\n\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"encode page from database\", \"offset\", offset, \"pgno\", pgno)\n\n\t\t// Otherwise read directly from the database file.\n\t\tif _, err := db.f.ReadAt(data, offset); err != nil {\n\t\t\treturn fmt.Errorf(\"read database page %d: %w\", pgno, err)\n\t\t}\n\t\tif err := enc.EncodePage(ltx.PageHeader{Pgno: pgno}, data); err != nil {","sourceCodeStart":2291,"sourceCodeEnd":2327,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2291-L2327","documentation":"While building an LTX file from the database and WAL (snapshot path in writeLTXFromDB), litestream reads a page's bytes from the WAL file at the recorded frame offset. A ReadAt failure is wrapped as 'read page %d @ %d:' with the page number and byte offset so you know exactly which WAL frame could not be read.","triggerScenarios":"walFile.ReadAt(data, offset+WALFrameHeaderSize) at db.go:2308 returned an error during snapshot LTX encoding: WAL truncated/shrunk concurrently by another checkpoint process, WAL deleted or rotated externally, or an I/O error on the WAL file.","commonSituations":"Two litestream processes (or litestream plus a manual checkpoint) racing on the same database; WAL reset via `litestream reset` or manual deletion mid-sync; corrupted or truncated WAL after a crash; NFS serving stale file size.","solutions":["Ensure only one litestream process monitors the database (check for duplicate replicas/instances).","Do not truncate, move, or delete the -wal file manually while replication is running.","If the WAL is corrupt or was reset, run `litestream reset` for the database and let it rebuild from a replica or fresh snapshot.","Check WAL file integrity and disk health if EIO is reported."],"exampleFix":"// before\n# two agents on one db\nlitestream replicate -config /etc/a.yml &\nlitestream replicate -config /etc/b.yml &   # second process corrupts state\n// after\n# single process per database\nlitestream replicate -config /etc/litestream.yml","handlingStrategy":"retry","validationCode":"// ensure single writer\nif pidFileLocked() { throw new Error('another litestream instance is running') }","typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif isWALReadErr(err) {\n\t\t// verify single-process ownership; consider litestream reset\n\t}\n\treturn err\n}","preventionTips":["Run exactly one litestream process per database.","Never manually truncate/delete the -wal file during replication.","After WAL corruption, use `litestream reset` rather than patching files.","Monitor for external checkpointers touching the WAL."],"tags":["wal","read-error","snapshot","io"],"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"}