rqlite/rqlite · error
unmarshaling header: %w
Error message
unmarshaling header: %w
What it means
Restore successfully read the header bytes but UnmarshalSnapshotHeader could not decode them into a valid snapshot header, and Restore wraps that failure as 'unmarshaling header'. This library throws it because the header bytes are corrupt or not in the expected protobuf format, so the snapshot layout cannot be interpreted.
Source
Thrown at snapshot/restore.go:39
// Read header length (4 bytes, big-endian).
var hdrLenBuf [HeaderSizeLen]byte
n, err := io.ReadFull(r, hdrLenBuf[:])
totalRead += int64(n)
if err != nil {
return totalRead, fmt.Errorf("reading header length: %w", err)
}
hdrLen := binary.BigEndian.Uint32(hdrLenBuf[:])
// Read and parse header.
hdrBuf := make([]byte, hdrLen)
n, err = io.ReadFull(r, hdrBuf)
totalRead += int64(n)
if err != nil {
return totalRead, fmt.Errorf("reading header: %w", err)
}
hdr, err := UnmarshalSnapshotHeader(hdrBuf)
if err != nil {
return totalRead, fmt.Errorf("unmarshaling header: %w", err)
}
// The snapshot must be a full snapshot to extract a database.
full := hdr.GetFull()
if full == nil {
return totalRead, fmt.Errorf("snapshot has no database")
}
// Extract DB file. Wrap the source in a CRC32Reader so we can verify
// the bytes match the header's CRC32 without a second pass over disk.
dbFile, err := os.Create(dstPath)
if err != nil {
return totalRead, err
}
dbCR := rsum.NewCRC32Reader(r)
nr, err := io.CopyN(dbFile, dbCR, int64(full.DbHeader.SizeBytes))
totalRead += nrView on GitHub (pinned to 7586a4d1bd)
Solutions
- Confirm the input is an actual rqlite snapshot stream (produced by the snapshot writer), not a raw SQLite DB or a compressed artifact — decompress if ?compress was used.
- Inspect the wrapped UnmarshalSnapshotHeader error for protobuf decode specifics.
- Re-export a fresh snapshot from a healthy node and retry the restore.
- Verify checksums/integrity of the stored snapshot file before restoring.
Example fix
// before: feeding a raw sqlite file into Restore
f, _ := os.Open("backup.db")
n, err := snapshot.Restore(f, dst)
// after: only restore snapshot streams; handle compression
var r io.Reader = snapshotStream
if compressed { r, _ = gzip.NewReader(snapshotStream) }
n, err = snapshot.Restore(r, dst) Defensive patterns
Strategy: validation
Validate before calling
// ensure the input is a rqlite snapshot stream, decompressing if needed
var r io.Reader = raw
if compressed { zr, err := gzip.NewReader(raw); if err != nil { return err }; r = zr }
magic, _ := peekBytes(r, 4)
if isRawSQLiteFile(magic) { return fmt.Errorf("input is a raw DB, not a snapshot stream") } Try / catch
n, err := snapshot.Restore(r, dst)
if err != nil && strings.Contains(err.Error(), "unmarshaling header") {
return fmt.Errorf("source is not a valid rqlite snapshot; re-export it")
} Prevention
- Only feed Restore streams produced by the snapshot writer
- Decompress compressed backups before restoring
- Checksum stored snapshots and validate before use
When it happens
Trigger: Calling snapshot.Restore with a stream whose header region contains garbage: corrupted file, wrong format (e.g. raw SQLite file instead of a rqlite snapshot stream), or bytes scrambled by an incompatible writer version.
Common situations: Attempting to restore a plain .db file (or a gzipped/compressed snapshot) directly through Restore without decompression/format conversion; bit rot on stored snapshots; passing output of a mismatched rqlite version.
Related errors
- error restoring database from snapshot: %v
- protobuf unmarshal: %w
- unknown operation type: %s
- reading header length: %w
- reading header: %w
AI-assisted analysis of rqlite/rqlite@7586a4d1bd (2026-09-03).
Data as JSON: /api/errors/cda144367108edaa.
Report an issue: GitHub.