benbjohnson/litestream · error
unsupported wal version: %d
Error message
unsupported wal version: %d
What it means
SQLite embeds a format version (3007000, i.e. SQLite 3.7.0) at bytes 4-8 of the WAL header. Litestream rejects WAL files written with any other version, as their frame format cannot be assumed compatible. Note this check only runs after the header checksum passes, so the version field is trusted.
Source
Thrown at wal_reader.go:123
case 0x377f0682:
r.bo = binary.LittleEndian
case 0x377f0683:
r.bo = binary.BigEndian
default:
return fmt.Errorf("invalid wal header magic: %x", magic)
}
// If the header checksum doesn't match then we may have failed with
// a partial WAL header write during checkpointing.
chksum1 := binary.BigEndian.Uint32(hdr[24:])
chksum2 := binary.BigEndian.Uint32(hdr[28:])
if v0, v1 := WALChecksum(r.bo, 0, 0, hdr[:24]); v0 != chksum1 || v1 != chksum2 {
return io.EOF
}
// Verify version is correct.
if version := binary.BigEndian.Uint32(hdr[4:]); version != 3007000 {
return fmt.Errorf("unsupported wal version: %d", version)
}
r.pageSize = binary.BigEndian.Uint32(hdr[8:])
r.seq = binary.BigEndian.Uint32(hdr[12:])
r.salt1 = binary.BigEndian.Uint32(hdr[16:])
r.salt2 = binary.BigEndian.Uint32(hdr[20:])
r.chksum1, r.chksum2 = chksum1, chksum2
return nil
}
// ReadFrame reads the next frame from the WAL and returns the page number.
// Returns io.EOF at the end of the valid WAL.
func (r *WALReader) ReadFrame(ctx context.Context, data []byte) (pgno, commit uint32, err error) {
return r.readFrame(ctx, data, true)
}
func (r *WALReader) readFrame(ctx context.Context, data []byte, verifyChecksum bool) (pgno, commit uint32, err error) {View on GitHub (pinned to 4ed7a308f6)
Solutions
- Verify the SQLite version writing the WAL — standard releases use version 3007000 and should never trigger this
- Hexdump the header (xxd -l 32 file) and inspect bytes 4-8 for the expected 00 2d e2 18 (3007000)
- If a custom SQLite build is in use, rebuild against stock SQLite
- Restore from replica and re-replicate if the WAL is corrupt
Defensive patterns
Strategy: try-catch
Validate before calling
hdr := make([]byte, 8)
io.ReadFull(f, hdr)
if binary.BigEndian.Uint32(hdr[4:]) != 3007000 {
return fmt.Errorf("unsupported WAL version %d; need stock SQLite WAL format", binary.BigEndian.Uint32(hdr[4:]))
} Try / catch
r, err := NewWALReader(ctx, f, logger)
if err != nil && strings.Contains(err.Error(), "unsupported wal version") {
return fmt.Errorf("WAL written by non-standard SQLite build: %w", err)
} Prevention
- Use official SQLite releases (or modernc.org/sqlite) — stock WALs are always version 3007000
- Never point the reader at hand-crafted or tool-generated files
- Rebuild any custom SQLite fork against the standard WAL format
- Treat this error as a signal of file corruption or wrong-file-type, not a runtime condition
When it happens
Trigger: Reading a WAL file whose version field is not exactly 3007000 — practically only possible with a future/hypothetical SQLite WAL format change, a manually crafted file, or corruption that happens to pass the checksum.
Common situations: Extremely rare; mostly seen when a non-SQLite tool wrote the file or a hand-built SQLite fork changed the WAL format; could also appear when pointing Litestream at an unrelated file that coincidentally has valid magic and checksum.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- set synchronous: %w
- checkpoint: %w
- checkpoint failed: %w
- sync database %s: %w
- enable wal failed, mode=%q
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/a089a91d1a74941c.
Report an issue: GitHub.