{"record":{"id":"a8166f9f1368cf2e","repo":"benbjohnson/litestream","slug":"new-wal-reader-after-reset","errorCode":null,"errorMessage":"new wal reader, after reset","messagePattern":"new wal reader, after reset","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2063,"sourceCode":"\tif err != nil {\n\t\treturn result, err\n\t}\n\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 {","sourceCodeStart":2045,"sourceCodeEnd":2081,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2045-L2081","documentation":"After a PrevFrameMismatchError, Litestream falls back to snapshotting by re-creating the reader from the WAL header; this error means even that fallback NewWALReader failed, so the WAL header itself is unreadable. Note the message omits %w, hiding the underlying cause.","triggerScenarios":"Prev frame mismatch triggered the offset reset to WALHeaderSize, and the subsequent NewWALReader still failed — i.e. the WAL file's header region is corrupt/truncated/empty despite the offset-based reader having read far enough to hit a mismatch.","commonSituations":"WAL concurrently truncated to zero by a RESTART checkpoint between the two reader constructions; disk corruption affecting the header page; race where the -wal file is replaced mid-sync.","solutions":["Retry the sync — a transient race with SQLite WAL restart usually resolves next iteration","Inspect the WAL header bytes; if corrupt, force SQLite to rebuild it via PRAGMA wal_checkpoint(TRUNCATE)","Check disk health if failures persist","File an issue — this message drops the wrapped cause, so also consider improving it to include the underlying error"],"exampleFix":"// before\nreturn result, fmt.Errorf(\"new wal reader, after reset\")\n// after\nreturn result, fmt.Errorf(\"new wal reader, after reset: %w\", err)","handlingStrategy":"retry","validationCode":"// confirm WAL header is readable before assuming transient\nf, err := os.Open(dbPath + \"-wal\")\nif err == nil {\n\thdr := make([]byte, 32)\n\tn, _ := f.Read(hdr)\n\tf.Close()\n\tif n < 32 { /* WAL mid-restart; wait and retry */ }\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"new wal reader, after reset\") {\n\ttime.Sleep(retryInterval) // likely raced with a WAL restart\n\tretrySync()\n}","preventionTips":["Accept that this can race with SQLite RESTART checkpoints; retry usually fixes it","Fix the error message in a PR to include %w for diagnosability","Check volume error logs if it recurs"],"tags":["sqlite","wal","corruption"],"backgroundTag":"checksum-mismatch","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"}