{"record":{"id":"0403d696f1d96b8c","repo":"benbjohnson/litestream","slug":"read-database-page-d-w","errorCode":null,"errorMessage":"read database page %d: %w","messagePattern":"read database page (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2325,"sourceCode":"\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 {\n\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc (db *DB) writeLTXFromWAL(ctx context.Context, enc *ltx.Encoder, walFile *os.File, prevCommit, commit uint32, pageMap map[uint32]int64) error {\n\t// Create an ordered list of page numbers since the LTX encoder requires it.\n\tpgnos := make([]uint32, 0, len(pageMap))\n\tfor pgno := range pageMap {\n\t\tpgnos = append(pgnos, pgno)\n\t}\n\tlockPgno := ltx.LockPgno(uint32(db.pageSize))\n\tif commit > prevCommit {\n\t\twalPgnoN := len(pgnos)","sourceCodeStart":2307,"sourceCodeEnd":2343,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2307-L2343","documentation":"During snapshot LTX generation, pages not present in the WAL are read directly from the main database file at offset (pgno-1)*pageSize. A failed ReadAt is wrapped as 'read database page %d:' with the page number, meaning the database file could not supply a page that the WAL commit count says exists.","triggerScenarios":"db.f.ReadAt(data, offset) at db.go:2324 returned an error: pgno exceeds the database file size (commit count from WAL disagrees with the main DB file, e.g. after external truncation), the DB file was deleted/replaced mid-sync, or an I/O error on the storage device.","commonSituations":"Database file truncated or replaced while litestream holds it open; mismatched DB/WAL pair after copying files out of band; corrupt or failing disk sectors; read errors on network-backed storage.","solutions":["Verify the database file size covers the page count implied by the WAL commit (`PRAGMA page_count;` vs file size).","Never replace, truncate, or move the database file while litestream is replicating; stop litestream first.","Check disk/storage health for EIO; move to healthy storage if the device is failing.","Restore a consistent DB+WAL pair from a replica and restart replication."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const size = fs.statSync(dbPath).size\nconst pageCount = Math.floor(size / pageSize)\nif (pageCount < walCommitCount) throw new Error('DB file smaller than WAL commit; inconsistent pair')","typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"read database page\") {\n\t\t// check DB file size vs page_count; restore consistent pair\n\t}\n\treturn err\n}","preventionTips":["Stop litestream before replacing or truncating the database file.","Always pair the exact DB and WAL files; never mix generations.","Monitor storage device health for read errors.","Keep the database on reliable local storage."],"tags":["database-file","read-error","io","snapshot"],"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"}