rqlite/rqlite · error
unsupported wal version: %d
Error message
unsupported wal version: %d
What it means
Reader.ReadHeader validates the WAL file header, including the WAL format version stored at bytes 4-8 of the 32-byte header. This error is returned when the version read from the WAL file does not equal WALSupportedVersion, meaning the library cannot safely parse this WAL format. It prevents corrupt or incompatible WAL files from being misinterpreted.
Source
Thrown at db/wal/reader.go:136
case 0x377f0683:
r.bo = binary.BigEndian
default:
return fmt.Errorf("invalid wal header magic: %x", r.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, err := WALChecksum(r.bo, 0, 0, hdr[:24]); err != nil {
return err
} else if v0 != chksum1 || v1 != chksum2 {
return io.EOF
}
// Verify version is correct.
if version := binary.BigEndian.Uint32(hdr[4:]); version != WALSupportedVersion {
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 returns the next page number and commit offset from the WAL. If
// data is not nil, then the page data is read into the buffer and the checksum
// is verified. If data is nil, then the page data is skipped and no checksum
// verification is performed. Returns io.EOF at the end of the valid WAL.
func (r *Reader) ReadFrame(data []byte) (pgno, commit uint32, err error) {
if data != nil && len(data) != int(r.pageSize) {
return 0, 0, fmt.Errorf("WALReader.ReadFrame(): buffer size (%d) must match page size (%d)", len(data), r.pageSize)View on GitHub (pinned to 7586a4d1bd)
Solutions
- Verify the WAL file is a genuine SQLite WAL whose magic header is 0x377f0682/0x377f0683 and whose version bytes match WALSupportedVersion
- Regenerate the WAL by checkpointing with the SQLite version that matches the supported format, then retry
- Check for file corruption or truncation (wrong byte offsets) and restore the WAL from backup
- Confirm you are reading the correct file path and not a DB or temp file
Example fix
// before: blindly scanning whatever file was passed
r, err := wal.OpenReaderAt(path)
sc, err := wal.NewFullScanner(r)
// after: check the header version first
hdr := make([]byte, 32)
f, _ := os.Open(path)
io.ReadFull(f, hdr)
if binary.BigEndian.Uint32(hdr[4:]) != wal.WALSupportedVersion {
return fmt.Errorf("WAL version %d unsupported; regenerate WAL", binary.BigEndian.Uint32(hdr[4:]))
} Defensive patterns
Strategy: validation
Validate before calling
func validateWALVersion(path string) error {
f, err := os.Open(path)
if err != nil { return err }
defer f.Close()
hdr := make([]byte, 32)
if _, err := io.ReadFull(f, hdr); err != nil { return err }
magic := binary.BigEndian.Uint32(hdr[0:4])
if magic != 0x377f0682 && magic != 0x377f0683 {
return fmt.Errorf("not a SQLite WAL file: magic %x", magic)
}
if v := binary.BigEndian.Uint32(hdr[4:8]); v != wal.WALSupportedVersion {
return fmt.Errorf("unsupported WAL version %d (want %d)", v, wal.WALSupportedVersion)
}
return nil
} Type guard
func isSupportedWAL(hdr []byte) bool {
return len(hdr) >= 32 &&
(binary.BigEndian.Uint32(hdr[0:4]) == 0x377f0682 ||
binary.BigEndian.Uint32(hdr[0:4]) == 0x377f0683) &&
binary.BigEndian.Uint32(hdr[4:8]) == wal.WALSupportedVersion
} Try / catch
if err := validateWALVersion(walPath); err != nil {
// quarantine and skip, or regenerate via checkpoint
log.Printf("skipping WAL %s: %v", walPath, err)
} else if sc, err := wal.NewFullScanner(r); err != nil {
varunsupported := strings.Contains(err.Error(), "unsupported wal version")
if !unsupported { return err }
} Prevention
- Always verify WAL magic and version bytes before handing files to the scanner
- Do not mix WAL files across rqlite/SQLite versions with different WAL formats
- Keep checksums/backups of WAL files; treat version errors as corruption signals
When it happens
Trigger: Calling NewFullScanner or NewCompactingFrameScanner (both call ReadHeader) on a WAL file whose header version field differs from WALSupportedVersion. This can also occur if the WAL file is corrupted such that the version bytes are garbage, or if the file is not actually a SQLite WAL file.
Common situations: Opening a WAL file produced by a newer/older SQLite version with a different WAL format version; pointing the reader at a truncated, zero-filled, or corrupted WAL file; accidentally passing a database file or other binary instead of the -wal file.
Related errors
- ErrWALStillExists
- ErrOpenTransaction
- ErrZeroPageNumber
- installed WAL file is not a valid SQLite WAL file
- no WAL data available for snapshot
AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03).
Data as JSON: /api/errors/a527c4ef60286cc7.
Report an issue: GitHub.