{"record":{"id":"5bf95b1b434f379d","repo":"benbjohnson/litestream","slug":"offset-d-must-be-greater-than-the-wal-header-si","errorCode":null,"errorMessage":"offset (%d) must be greater than the wal header size (%d)","messagePattern":"offset \\((.+?)\\) must be greater than the wal header size \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"wal_reader.go","lineNumber":48,"sourceCode":"\tlogger *slog.Logger\n}\n\n// NewWALReader returns a new instance of WALReader.\nfunc NewWALReader(rd io.ReaderAt, logger *slog.Logger) (*WALReader, error) {\n\tr := &WALReader{r: rd, logger: logger}\n\tif err := r.readHeader(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn r, nil\n}\n\n// NewWALReaderWithOffset returns a new instance of WALReader at a given offset.\n// Salt must match or else no frames will be returned. Checksum calculated from\n// from previous page.\nfunc NewWALReaderWithOffset(ctx context.Context, rd io.ReaderAt, offset int64, salt1, salt2 uint32, logger *slog.Logger) (*WALReader, error) {\n\t// Ensure we are not starting on the first page since we need to read the previous.\n\tif offset <= WALHeaderSize {\n\t\treturn nil, fmt.Errorf(\"offset (%d) must be greater than the wal header size (%d)\", offset, WALHeaderSize)\n\t}\n\n\tr := &WALReader{r: rd, logger: logger}\n\n\t// Read header to determine page size & byte order.\n\tif err := r.readHeader(); err != nil {\n\t\treturn nil, fmt.Errorf(\"read header: %w\", err)\n\t}\n\n\t// Load in salt in case the beginning of the file has been overwritten.\n\tr.salt1, r.salt2 = salt1, salt2\n\n\t// Ensure offset is positioned on a frame start.\n\tframeSize := int64(r.pageSize + WALFrameHeaderSize)\n\tif (offset-WALHeaderSize)%frameSize != 0 {\n\t\treturn nil, fmt.Errorf(\"unaligned wal offset %d for page size %d\", offset, r.pageSize)\n\t}\n\tr.frameN = int((offset - WALHeaderSize) / frameSize)","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/wal_reader.go#L30-L66","documentation":"NewWALReaderWithOffset resumes reading the WAL at a given frame offset, but it must read the previous page to seed the checksum, so the offset must be strictly greater than the WAL header size (32 bytes). Offsets at or below the header size are rejected because there is no preceding page to compute the running checksum from.","triggerScenarios":"Calling NewWALReaderWithOffset with offset <= WALHeaderSize — e.g. offset 0 or 32 — from the DB's sync path, typically when the saved sync offset was never advanced past the WAL header or was reset/corrupted.","commonSituations":"A freshly created WAL whose stored offset was persisted as 0; corrupt or truncated checkpoint metadata; calling the public constructor directly with a hand-computed offset that includes the header.","solutions":["Ensure the offset passed reflects the start of a frame after the 32-byte WAL header (offset > 32)","Use NewWALReader (header-based) instead when starting from the beginning of the WAL","Reset the DB's stored sync offset (e.g. via litestream reset) if persisted state is corrupt and re-sync"],"exampleFix":"// before\nr, err := NewWALReaderWithOffset(ctx, rd, 0, salt1, salt2, logger)\n// after\nr, err := NewWALReader(ctx, rd, logger) // start from header\n// or: offset must point at a frame beyond the 32-byte header","handlingStrategy":"validation","validationCode":"if offset <= WALHeaderSize {\n    // fall back to NewWALReader which starts from the header\n    return NewWALReader(ctx, rd, logger)\n}","typeGuard":"func validSyncOffset(offset int64) bool { return offset > WALHeaderSize }","tryCatchPattern":null,"preventionTips":["Persist the sync offset only after successfully reading frames (never 0)","Use NewWALReader for initial reads and WithOffset only for resumption","Guard public constructor calls with an offset > WALHeaderSize check"],"tags":["go","wal","offset","validation"],"backgroundTag":"value-out-of-range","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}