benbjohnson/litestream · error

new wal reader with offset: %w

Error message

new wal reader with offset: %w

What it means

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.

Source

Thrown at db.go:2066

	defer walFile.Close()

	walReaderLogger := db.Logger.With(LogKeySubsystem, LogSubsystemWALReader)
	var rd *WALReader
	if info.offset == WALHeaderSize {
		if rd, err = NewWALReader(walFile, walReaderLogger); err != nil {
			return result, fmt.Errorf("new wal reader: %w", err)
		}
	} else {
		// If we cannot verify the previous frame
		var pfmError *PrevFrameMismatchError
		if rd, err = NewWALReaderWithOffset(ctx, walFile, info.offset, info.salt1, info.salt2, walReaderLogger); errors.As(err, &pfmError) {
			db.Logger.Log(ctx, internal.LevelTrace, "prev frame mismatch, snapshotting", "err", pfmError.Err)
			info.offset = WALHeaderSize
			if rd, err = NewWALReader(walFile, walReaderLogger); err != nil {
				return result, fmt.Errorf("new wal reader, after reset")
			}
		} else if err != nil {
			return result, fmt.Errorf("new wal reader with offset: %w", err)
		}
	}

	// Build a mapping of changed page numbers and their latest content.
	db.setSyncDiagPhase(diagPhaseSyncPageMap,
		func(s *diagState) {
			s.txID = txID
			s.snapshotting = info.snapshotting
			s.reason = info.reason
		})
	if info.snapshotting {
		maxSyncWALBytes = 0
	}
	pageMap, maxOffset, walCommit, limited, err := rd.pageMap(ctx, maxSyncWALBytes)
	if err != nil {
		return result, fmt.Errorf("page map: %w", err)
	}
	result.limited = limited

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Run `litestream reset <dbpath>` to discard stale offsets and force a full resnapshot
  2. Check for duplicate Litestream processes replicating the same DB
  3. Verify WAL file size is >= info.offset; if truncated, a checkpoint occurred — resnapshot is expected
  4. Check disk/filesystem health for repeated read errors

Example fix

// before
$ ls -l /data/app.db-wal   # smaller than recorded offset
// after
$ litestream reset /data/app.db && systemctl restart litestream
Defensive patterns

Strategy: fallback

Validate before calling

st, err := os.Stat(dbPath + "-wal")
usable := err == nil && st.Size() >= int64(headerSize) // if WAL smaller than saved offset, resnapshot is required

Try / catch

if err != nil && strings.Contains(err.Error(), "new wal reader with offset") {
	exec.Command("litestream", "reset", dbPath).Run() // discard stale offsets
	restartReplication()
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/387e3189098f35b4. Report an issue: GitHub.