{"record":{"id":"387e3189098f35b4","repo":"benbjohnson/litestream","slug":"new-wal-reader-with-offset-w","errorCode":null,"errorMessage":"new wal reader with offset: %w","messagePattern":"new wal reader with offset: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2066,"sourceCode":"\tdefer walFile.Close()\n\n\twalReaderLogger := db.Logger.With(LogKeySubsystem, LogSubsystemWALReader)\n\tvar rd *WALReader\n\tif info.offset == WALHeaderSize {\n\t\tif rd, err = NewWALReader(walFile, walReaderLogger); err != nil {\n\t\t\treturn result, fmt.Errorf(\"new wal reader: %w\", err)\n\t\t}\n\t} else {\n\t\t// If we cannot verify the previous frame\n\t\tvar pfmError *PrevFrameMismatchError\n\t\tif rd, err = NewWALReaderWithOffset(ctx, walFile, info.offset, info.salt1, info.salt2, walReaderLogger); errors.As(err, &pfmError) {\n\t\t\tdb.Logger.Log(ctx, internal.LevelTrace, \"prev frame mismatch, snapshotting\", \"err\", pfmError.Err)\n\t\t\tinfo.offset = WALHeaderSize\n\t\t\tif rd, err = NewWALReader(walFile, walReaderLogger); err != nil {\n\t\t\t\treturn result, fmt.Errorf(\"new wal reader, after reset\")\n\t\t\t}\n\t\t} else if err != nil {\n\t\t\treturn result, fmt.Errorf(\"new wal reader with offset: %w\", err)\n\t\t}\n\t}\n\n\t// Build a mapping of changed page numbers and their latest content.\n\tdb.setSyncDiagPhase(diagPhaseSyncPageMap,\n\t\tfunc(s *diagState) {\n\t\t\ts.txID = txID\n\t\t\ts.snapshotting = info.snapshotting\n\t\t\ts.reason = info.reason\n\t\t})\n\tif info.snapshotting {\n\t\tmaxSyncWALBytes = 0\n\t}\n\tpageMap, maxOffset, walCommit, limited, err := rd.pageMap(ctx, maxSyncWALBytes)\n\tif err != nil {\n\t\treturn result, fmt.Errorf(\"page map: %w\", err)\n\t}\n\tresult.limited = limited","sourceCodeStart":2048,"sourceCodeEnd":2084,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2048-L2084","documentation":"NewWALReaderWithOffset failed with an error that is not a PrevFrameMismatchError while resuming WAL reads at the last-known offset. The offset path verifies the frame preceding info.offset matches the recorded salt1/salt2; any other read/validation failure is wrapped here.","triggerScenarios":"Resuming a sync from info.offset where the WAL content differs in a way not classified as prev-frame mismatch: read error, short file (offset beyond EOF), invalid frame header, or context cancellation.","commonSituations":"WAL was checkpointed/truncated so the saved offset is beyond current EOF; another Litestream instance moved the position; on-disk corruption; salts changed without pfm classification.","solutions":["Run `litestream reset <dbpath>` to discard stale offsets and force a full resnapshot","Check for duplicate Litestream processes replicating the same DB","Verify WAL file size is >= info.offset; if truncated, a checkpoint occurred — resnapshot is expected","Check disk/filesystem health for repeated read errors"],"exampleFix":"// before\n$ ls -l /data/app.db-wal   # smaller than recorded offset\n// after\n$ litestream reset /data/app.db && systemctl restart litestream","handlingStrategy":"fallback","validationCode":"st, err := os.Stat(dbPath + \"-wal\")\nusable := err == nil && st.Size() >= int64(headerSize) // if WAL smaller than saved offset, resnapshot is required","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"new wal reader with offset\") {\n\texec.Command(\"litestream\", \"reset\", dbPath).Run() // discard stale offsets\n\trestartReplication()\n}","preventionTips":["Don't restore/truncate the WAL outside Litestream's control","One replicator per DB to avoid divergent offsets","Enable `auto-recover` on replicas where unattended recovery is desired"],"tags":["sqlite","wal","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"}