{"record":{"id":"1c6b6539204f93b2","repo":"benbjohnson/litestream","slug":"invalid-wal-header-magic-x","errorCode":null,"errorMessage":"invalid wal header magic: %x","messagePattern":"invalid wal header magic: %x","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"wal_reader.go","lineNumber":110,"sourceCode":"\n// readHeader reads the WAL header into the reader. Returns io.EOF if WAL is invalid.\nfunc (r *WALReader) readHeader() error {\n\t// If we have a partial WAL, then mark WAL as done.\n\thdr := make([]byte, WALHeaderSize)\n\tif n, err := r.r.ReadAt(hdr, 0); n < len(hdr) {\n\t\treturn io.EOF\n\t} else if err != nil {\n\t\treturn err\n\t}\n\n\t// Determine byte order of checksums.\n\tswitch magic := binary.BigEndian.Uint32(hdr[0:]); magic {\n\tcase 0x377f0682:\n\t\tr.bo = binary.LittleEndian\n\tcase 0x377f0683:\n\t\tr.bo = binary.BigEndian\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid wal header magic: %x\", magic)\n\t}\n\n\t// If the header checksum doesn't match then we may have failed with\n\t// a partial WAL header write during checkpointing.\n\tchksum1 := binary.BigEndian.Uint32(hdr[24:])\n\tchksum2 := binary.BigEndian.Uint32(hdr[28:])\n\tif v0, v1 := WALChecksum(r.bo, 0, 0, hdr[:24]); v0 != chksum1 || v1 != chksum2 {\n\t\treturn io.EOF\n\t}\n\n\t// Verify version is correct.\n\tif version := binary.BigEndian.Uint32(hdr[4:]); version != 3007000 {\n\t\treturn fmt.Errorf(\"unsupported wal version: %d\", version)\n\t}\n\n\tr.pageSize = binary.BigEndian.Uint32(hdr[8:])\n\tr.seq = binary.BigEndian.Uint32(hdr[12:])\n\tr.salt1 = binary.BigEndian.Uint32(hdr[16:])","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/wal_reader.go#L92-L128","documentation":"readHeader validates that the first 4 bytes of the WAL file are SQLite's WAL magic numbers 0x377f0682 (little-endian) or 0x377f0683 (big-endian). Any other value means the file being read is not a valid SQLite WAL, or its header is corrupt/truncated.","triggerScenarios":"Opening a file as a WAL that is actually something else (a database file, an LTX file, a log, an empty file with garbage); reading a WAL whose first bytes were overwritten by disk corruption or an interrupted header write.","commonSituations":"Misconfigured WAL path in config pointing at the .db file instead of the -wal file; copying/mounting volumes while SQLite was mid-checkpoint; testing the reader against synthetic or placeholder files.","solutions":["Confirm the path passed to the reader is the '-wal' file, not the main database file","Check the first 4 bytes of the file (xxd -l 4 file) — they must be 37 7f 06 82 or 37 7f 06 83","If corruption is suspected, restore from the latest replica and let SQLite recreate the WAL","Ensure no other process truncated or rewrote the file while it was being read"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"hdr := make([]byte, 4)\nf, _ := os.Open(walPath)\nio.ReadFull(f, hdr)\nmagic := binary.BigEndian.Uint32(hdr)\nif magic != 0x377f0682 && magic != 0x377f0683 {\n    return fmt.Errorf(\"%s is not a SQLite WAL file (magic %x)\", walPath, magic)\n}","typeGuard":"func isWALMagic(b []byte) bool {\n    m := binary.BigEndian.Uint32(b[:4])\n    return m == 0x377f0682 || m == 0x377f0683\n}","tryCatchPattern":"r, err := NewWALReader(ctx, f, logger)\nif err != nil && strings.Contains(err.Error(), \"invalid wal header magic\") {\n    return fmt.Errorf(\"%s is not a WAL file — check configured path: %w\", f.Name(), err)\n}","preventionTips":["Confirm the configured path points to the '-wal' file, not the database itself","Verify magic bytes 37 7f 06 82/83 before parsing third-party files","Avoid reading the WAL during concurrent checkpoint/truncation","Restore from replica when the header is corrupt instead of forcing a read"],"tags":["sqlite","wal","corruption","magic-number"],"backgroundTag":"invalid-argument-value","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"}