{"record":{"id":"1021c0e0cbd46733","repo":"benbjohnson/litestream","slug":"short-read-page-d-d","errorCode":null,"errorMessage":"short read page %d @ %d","messagePattern":"short read page (.+?) @ (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2311,"sourceCode":"\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 {\n\t\t\treturn fmt.Errorf(\"encode ltx frame (pgno=%d): %w\", pgno, err)\n\t\t}","sourceCodeStart":2293,"sourceCodeEnd":2329,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2293-L2329","documentation":"ReadAt returned fewer bytes than a full database page from the WAL frame at the given offset. Litestream treats a partial page read as a hard error ('short read page %d @ %d') because an LTX frame must contain a complete page; a truncated frame means the WAL's page map no longer matches the file contents.","triggerScenarios":"walFile.ReadAt at db.go:2310 returned n != pageSize during snapshot encoding: the WAL was truncated or overwritten between building the pageMap and reading frames, an offset in pageMap points past the current WAL end (stale map after external WAL modification), or the WAL file is corrupt/truncated.","commonSituations":"External processes running `PRAGMA wal_checkpoint(TRUNCATE)` or deleting the WAL mid-snapshot; stale salt/frame mismatch after WAL reset (issue #927 family); crashed writer leaving a partially written WAL; copying DB+WAL files while litestream runs.","solutions":["Stop external checkpointing/WAL manipulation while litestream runs; do not copy hot database files.","Restart litestream (or `litestream reset` for the DB) to rebuild the page map from the current WAL.","Check whether another litestream version or process is checkpointing concurrently and remove the duplicate.","If the WAL is physically truncated by a crash, restore from a replica and re-seed."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"short read page\") {\n\t\t// WAL changed under us: stop external writers, reset/rebuild\n\t}\n\treturn err\n}","preventionTips":["Don't copy hot DB/WAL files while litestream runs; use the replica for backups.","Suppress external PRAGMA wal_checkpoint(TRUNCATE) during replication.","Detect stale WAL (salt mismatch) early and reset local state.","Keep DB and WAL on the same healthy volume."],"tags":["wal","short-read","truncation","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"}